diff --git a/.flake8 b/.flake8 index 5a2ed0b5b7f..eee32e982a8 100644 --- a/.flake8 +++ b/.flake8 @@ -5,6 +5,6 @@ doctests = True # W503 and W504 are mutually exclusive. PEP 8 recommends line break before. ignore = W503,E203 -max-complexity = 20 +max-complexity = 30 max-line-length = 120 select = E,W,F,C,N diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d07ef88044d..eff3343620c 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -54,11 +54,13 @@ /libraries/ArduinoOTA/ @me-no-dev /libraries/AsyncUDP/ @me-no-dev /libraries/BLE/ @lucasssvaz @SuGlider +/libraries/ESP_HostedOTA/ @me-no-dev /libraries/ESP_I2S/ @me-no-dev /libraries/ESP_NOW/ @P-R-O-C-H-Y @lucasssvaz /libraries/ESP_SR/ @me-no-dev /libraries/ESPmDNS/ @me-no-dev /libraries/Ethernet/ @me-no-dev +/libraries/Hash/ @lucasssvaz /libraries/Matter/ @SuGlider /libraries/NetBIOS/ @me-no-dev /libraries/Network/ @me-no-dev @@ -72,9 +74,9 @@ /libraries/Wire/ @me-no-dev /libraries/Zigbee/ @P-R-O-C-H-Y -# CI JSON +# CI YAML # Keep this after other libraries and tests to avoid being overridden. -**/ci.json @lucasssvaz +**/ci.yml @lucasssvaz # The CODEOWNERS file should be owned by the developers of the ESP32 Arduino Core. # Leave this entry as the last one to avoid being overridden. diff --git a/.github/ISSUE_TEMPLATE/Issue-report.yml b/.github/ISSUE_TEMPLATE/Issue-report.yml index 97834925020..76cac3d612a 100644 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ b/.github/ISSUE_TEMPLATE/Issue-report.yml @@ -40,9 +40,12 @@ body: label: Version description: What version of Arduino ESP32 are you running? If possible, consider updating to the latest version. options: - - latest stable Release (if not listed below) - - latest development Release Candidate (RC-X) + - Please select a version from the list below - latest master (checkout manually) + - v3.3.4 + - v3.3.3 + - v3.3.2 + - v3.3.1 - v3.3.0 - v3.2.1 - v3.2.0 @@ -58,26 +61,7 @@ body: - v3.0.2 - v3.0.1 - v3.0.0 - - v2.0.17 - - v2.0.16 - - v2.0.15 - - v2.0.14 - - v2.0.13 - - v2.0.12 - - v2.0.11 - - v2.0.10 - - v2.0.9 - - v2.0.8 - - v2.0.7 - - v2.0.6 - - v2.0.5 - - v2.0.4 - - v2.0.3 - - v2.0.2 - - v2.0.1 - - v2.0.0 - - v1.0.6 - - other + - Older versions validations: required: true - type: dropdown diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5e0d99c0457..6a803943690 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ ## Description of Change Please describe your proposed Pull Request and it's impact. -## Tests scenarios +## Test Scenarios Please describe on what Hardware and Software combinations you have tested this Pull Request and how. (*eg. I have tested my Pull Request on Arduino-esp32 core v2.0.2 with ESP32 and ESP32-S2 Board with this scenario*) diff --git a/.github/pytools/Sign-File.ps1 b/.github/pytools/Sign-File.ps1 index 09094096ac7..b45b7149ac6 100755 --- a/.github/pytools/Sign-File.ps1 +++ b/.github/pytools/Sign-File.ps1 @@ -19,7 +19,7 @@ function FindSignTool { if (Test-Path -Path $SignTool -PathType Leaf) { return $SignTool } - $sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0" + $sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0", "10.0.14393.0", "10.0.15063.0", "10.0.16299.0", "10.0.17134.0", "10.0.26100.0" Foreach ($ver in $sdkVers) { $SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\${ver}\x64\signtool.exe" diff --git a/.github/scripts/find_all_boards.sh b/.github/scripts/find_all_boards.sh index 67b46661ca5..c37d306151e 100755 --- a/.github/scripts/find_all_boards.sh +++ b/.github/scripts/find_all_boards.sh @@ -8,7 +8,7 @@ boards_list=$(grep '.tarch=' boards.txt) while read -r line; do board_name=$(echo "$line" | cut -d '.' -f1 | cut -d '#' -f1) # skip esp32c2 as we dont build libs for it - if [ "$board_name" == "esp32c2" ]; then + if [ "$board_name" == "esp32c2" ] || [ "$board_name" == "esp32c61" ]; then echo "Skipping 'espressif:esp32:$board_name'" continue fi diff --git a/.github/scripts/find_new_boards.sh b/.github/scripts/find_new_boards.sh index 4482aa2b1da..96d50cecfeb 100755 --- a/.github/scripts/find_new_boards.sh +++ b/.github/scripts/find_new_boards.sh @@ -27,12 +27,25 @@ echo "$modified_lines" boards_array=() previous_board="" +# Define excluded entries that are not actual boards +excluded_entries=("" "+" "-" "esp32_family" "menu") + # Extract board names from the modified lines, and add them to the boards_array while read -r line; do board_name=$(echo "$line" | cut -d '.' -f1 | cut -d '#' -f1) # remove + or - from the board name at the beginning board_name=${board_name#[-+]} - if [ "$board_name" != "" ] && [ "$board_name" != "+" ] && [ "$board_name" != "-" ] && [ "$board_name" != "esp32_family" ]; then + + # Check if board_name is in excluded entries + is_excluded=false + for excluded in "${excluded_entries[@]}"; do + if [ "$board_name" = "$excluded" ]; then + is_excluded=true + break + fi + done + + if [ "$is_excluded" = false ]; then if [ "$board_name" != "$previous_board" ]; then boards_array+=("espressif:esp32:$board_name") previous_board="$board_name" diff --git a/.github/scripts/generate_missing_junits.py b/.github/scripts/generate_missing_junits.py new file mode 100644 index 00000000000..1a2925ead04 --- /dev/null +++ b/.github/scripts/generate_missing_junits.py @@ -0,0 +1,330 @@ +#!/usr/bin/env python3 + +import json +import logging +import os +import re +import sys +from pathlib import Path +from xml.etree.ElementTree import Element, SubElement, ElementTree +import yaml + +# Configure logging +logging.basicConfig(level=logging.DEBUG, format='[%(levelname)s] %(message)s', stream=sys.stderr) + + +def parse_array(value) -> list[str]: + if isinstance(value, list): + return [str(x) for x in value] + if not isinstance(value, str): + return [] + txt = value.strip() + if not txt: + return [] + # Try JSON + try: + return [str(x) for x in json.loads(txt)] + except Exception as e: + logging.debug(f"Failed to parse value as JSON: {e}") + # Normalize single quotes then JSON + try: + fixed = txt.replace("'", '"') + return [str(x) for x in json.loads(fixed)] + except Exception as e: + logging.debug(f"Failed to parse value as JSON with quote normalization: {e}") + # Fallback: CSV + logging.debug(f"Falling back to CSV parsing for value: {txt}") + return [p.strip() for p in txt.strip("[]").split(",") if p.strip()] + + +def _parse_ci_yml(content: str) -> dict: + if not content: + return {} + try: + data = yaml.safe_load(content) or {} + if not isinstance(data, dict): + logging.warning("YAML content is not a dictionary, returning empty dict") + return {} + return data + except Exception as e: + logging.error(f"Failed to parse ci.yml content: {e}") + return {} + + +def _fqbn_counts_from_yaml(ci: dict) -> dict[str, int]: + counts: dict[str, int] = {} + if not isinstance(ci, dict): + return counts + fqbn = ci.get("fqbn") + if not isinstance(fqbn, dict): + return counts + for target, entries in fqbn.items(): + if isinstance(entries, list): + counts[str(target)] = len(entries) + elif entries is not None: + # Single value provided as string + counts[str(target)] = 1 + return counts + + +def _sdkconfig_meets(ci_cfg: dict, sdk_text: str) -> bool: + if not sdk_text: + return True + for req in ci_cfg.get("requires", []): + if not req or not isinstance(req, str): + continue + if not any(line.startswith(req) for line in sdk_text.splitlines()): + return False + req_any = ci_cfg.get("requires_any", []) + if req_any: + if not any(any(line.startswith(r.strip()) for line in sdk_text.splitlines()) for r in req_any if isinstance(r, str)): + return False + return True + + +def expected_from_artifacts(build_root: Path) -> dict[tuple[str, str, str, str], int]: + """Compute expected runs using ci.yml and sdkconfig found in build artifacts. + Returns mapping (platform, target, type, sketch) -> expected_count + """ + expected: dict[tuple[str, str, str, str], int] = {} + if not build_root.exists(): + return expected + print(f"[DEBUG] Scanning build artifacts in: {build_root}", file=sys.stderr) + for artifact_dir in build_root.iterdir(): + if not artifact_dir.is_dir(): + continue + m = re.match(r"test-bin-([A-Za-z0-9_\-]+)-([A-Za-z0-9_\-]+)", artifact_dir.name) + if not m: + continue + target = m.group(1) + test_type = m.group(2) + print(f"[DEBUG] Artifact group target={target} type={test_type} dir={artifact_dir}", file=sys.stderr) + + # Group build*.tmp directories by sketch + # Structure: test-bin--//build*.tmp/ + sketches_processed = set() + + # Find all build*.tmp directories and process each sketch once + for build_tmp in artifact_dir.rglob("build*.tmp"): + if not build_tmp.is_dir(): + continue + if not re.search(r"build\d*\.tmp$", build_tmp.name): + continue + + # Path structure is: test-bin--//build*.tmp/ + sketch = build_tmp.parent.name + + # Skip if we already processed this sketch + if sketch in sketches_processed: + continue + sketches_processed.add(sketch) + + print(f"[DEBUG] Processing sketch={sketch} from artifact {artifact_dir.name}", file=sys.stderr) + + ci_path = build_tmp / "ci.yml" + sdk_path = build_tmp / "sdkconfig" + + # Read ci.yml if it exists, otherwise use empty (defaults) + ci_text = "" + if ci_path.exists(): + try: + ci_text = ci_path.read_text(encoding="utf-8") + except Exception as e: + logging.warning(f"Failed to read ci.yml from {ci_path}: {e}") + else: + logging.debug(f"No ci.yml found at {ci_path}, using defaults") + + try: + sdk_text = sdk_path.read_text(encoding="utf-8", errors="ignore") if sdk_path.exists() else "" + except Exception as e: + logging.warning(f"Failed to read sdkconfig from {sdk_path}: {e}") + sdk_text = "" + + ci = _parse_ci_yml(ci_text) + fqbn_counts = _fqbn_counts_from_yaml(ci) + + # Determine allowed platforms for this test + # Performance tests are only run on hardware + if test_type == "performance": + allowed_platforms = ["hardware"] + else: + allowed_platforms = [] + platforms_cfg = ci.get("platforms") if isinstance(ci, dict) else None + for plat in ("hardware", "wokwi", "qemu"): + dis = None + if isinstance(platforms_cfg, dict): + dis = platforms_cfg.get(plat) + if dis is False: + continue + allowed_platforms.append(plat) + + # Requirements check + minimal = { + "requires": ci.get("requires") or [], + "requires_any": ci.get("requires_any") or [], + } + if not _sdkconfig_meets(minimal, sdk_text): + print(f"[DEBUG] Skip (requirements not met): target={target} type={test_type} sketch={sketch}", file=sys.stderr) + continue + + # Expected runs = number from fqbn_counts in ci.yml (how many FQBNs for this target) + exp_runs = fqbn_counts.get(target, 0) or 1 + print(f"[DEBUG] ci.yml specifies {exp_runs} FQBN(s) for target={target}", file=sys.stderr) + + for plat in allowed_platforms: + expected[(plat, target, test_type, sketch)] = exp_runs + print(f"[DEBUG] Expected: plat={plat} target={target} type={test_type} sketch={sketch} runs={exp_runs}", file=sys.stderr) + + if len(sketches_processed) == 0: + print(f"[DEBUG] No sketches found in this artifact group", file=sys.stderr) + return expected + + +def scan_executed_xml(xml_root: Path, valid_types: set[str]) -> dict[tuple[str, str, str, str], int]: + """Return executed counts per (platform, target, type, sketch). + Type/sketch/target are inferred from ...////.xml + """ + counts: dict[tuple[str, str, str, str], int] = {} + if not xml_root.exists(): + print(f"[DEBUG] Results root not found: {xml_root}", file=sys.stderr) + return counts + print(f"[DEBUG] Scanning executed XMLs in: {xml_root}", file=sys.stderr) + for xml_path in xml_root.rglob("*.xml"): + if not xml_path.is_file(): + continue + rel = str(xml_path) + platform = "hardware" + if "test-results-wokwi-" in rel: + platform = "wokwi" + elif "test-results-qemu-" in rel: + platform = "qemu" + # Expect ...////*.xml + parts = xml_path.parts + t_idx = -1 + for i, p in enumerate(parts): + if p in valid_types: + t_idx = i + if t_idx == -1 or t_idx + 3 >= len(parts): + continue + test_type = parts[t_idx] + sketch = parts[t_idx + 1] + target = parts[t_idx + 2] + key = (platform, target, test_type, sketch) + old_count = counts.get(key, 0) + counts[key] = old_count + 1 + print(f"[DEBUG] Executed XML #{old_count + 1}: plat={platform} target={target} type={test_type} sketch={sketch} file={xml_path.name}", file=sys.stderr) + print(f"[DEBUG] Executed entries discovered: {len(counts)}", file=sys.stderr) + return counts + + +def write_missing_xml(out_root: Path, platform: str, target: str, test_type: str, sketch: str, missing_count: int): + out_tests_dir = out_root / f"test-results-{platform}" / "tests" / test_type / sketch / target + out_tests_dir.mkdir(parents=True, exist_ok=True) + # Create one XML per missing index + for idx in range(missing_count): + suite_name = f"{test_type}_{platform}_{target}_{sketch}" + root = Element("testsuite", name=suite_name, tests="1", failures="0", errors="1") + case = SubElement(root, "testcase", classname=f"{test_type}.{sketch}", name="missing-run") + error = SubElement(case, "error", message="Expected test run missing") + error.text = "This placeholder indicates an expected test run did not execute." + tree = ElementTree(root) + out_file = out_tests_dir / f"{sketch}_missing_{idx}.xml" + tree.write(out_file, encoding="utf-8", xml_declaration=True) + + +def main(): + # Args: + if len(sys.argv) != 4: + print(f"Usage: {sys.argv[0]} ", file=sys.stderr) + return 2 + + build_root = Path(sys.argv[1]).resolve() + results_root = Path(sys.argv[2]).resolve() + out_root = Path(sys.argv[3]).resolve() + + # Validate inputs + if not build_root.is_dir(): + print(f"ERROR: Build artifacts directory not found: {build_root}", file=sys.stderr) + return 2 + if not results_root.is_dir(): + print(f"ERROR: Test results directory not found: {results_root}", file=sys.stderr) + return 2 + # Ensure output directory exists + try: + out_root.mkdir(parents=True, exist_ok=True) + except Exception as e: + print(f"ERROR: Failed to create output directory {out_root}: {e}", file=sys.stderr) + return 2 + + # Read matrices from environment variables injected by workflow + hw_enabled = (os.environ.get("HW_TESTS_ENABLED", "false").lower() == "true") + wokwi_enabled = (os.environ.get("WOKWI_TESTS_ENABLED", "false").lower() == "true") + qemu_enabled = (os.environ.get("QEMU_TESTS_ENABLED", "false").lower() == "true") + + hw_targets = parse_array(os.environ.get("HW_TARGETS", "[]")) + wokwi_targets = parse_array(os.environ.get("WOKWI_TARGETS", "[]")) + qemu_targets = parse_array(os.environ.get("QEMU_TARGETS", "[]")) + + hw_types = parse_array(os.environ.get("HW_TYPES", "[]")) + wokwi_types = parse_array(os.environ.get("WOKWI_TYPES", "[]")) + qemu_types = parse_array(os.environ.get("QEMU_TYPES", "[]")) + + expected = expected_from_artifacts(build_root) # (platform, target, type, sketch) -> expected_count + executed_types = set(hw_types + wokwi_types + qemu_types) + executed = scan_executed_xml(results_root, executed_types) # (platform, target, type, sketch) -> count + print(f"[DEBUG] Expected entries computed: {len(expected)}", file=sys.stderr) + + # Filter expected by enabled platforms and target/type matrices + enabled_plats = set() + if hw_enabled: + enabled_plats.add("hardware") + if wokwi_enabled: + enabled_plats.add("wokwi") + if qemu_enabled: + enabled_plats.add("qemu") + + # Build platform-specific target and type sets + plat_targets = { + "hardware": set(hw_targets), + "wokwi": set(wokwi_targets), + "qemu": set(qemu_targets), + } + plat_types = { + "hardware": set(hw_types), + "wokwi": set(wokwi_types), + "qemu": set(qemu_types), + } + + missing_total = 0 + extra_total = 0 + for (plat, target, test_type, sketch), exp_count in expected.items(): + if plat not in enabled_plats: + continue + # Check if target and type are valid for this specific platform + if target not in plat_targets.get(plat, set()): + continue + if test_type not in plat_types.get(plat, set()): + continue + got = executed.get((plat, target, test_type, sketch), 0) + if got < exp_count: + print(f"[DEBUG] Missing: plat={plat} target={target} type={test_type} sketch={sketch} expected={exp_count} got={got}", file=sys.stderr) + write_missing_xml(out_root, plat, target, test_type, sketch, exp_count - got) + missing_total += (exp_count - got) + elif got > exp_count: + print(f"[DEBUG] Extra runs: plat={plat} target={target} type={test_type} sketch={sketch} expected={exp_count} got={got}", file=sys.stderr) + extra_total += (got - exp_count) + + # Check for executed tests that were not expected at all + for (plat, target, test_type, sketch), got in executed.items(): + if (plat, target, test_type, sketch) not in expected: + print(f"[DEBUG] Unexpected test: plat={plat} target={target} type={test_type} sketch={sketch} got={got} (not in expected)", file=sys.stderr) + + print(f"Generated {missing_total} placeholder JUnit files for missing runs.", file=sys.stderr) + if extra_total > 0: + print(f"WARNING: {extra_total} extra test runs detected (more than expected).", file=sys.stderr) + + +if __name__ == "__main__": + sys.exit(main()) + + diff --git a/.github/scripts/get_affected.py b/.github/scripts/get_affected.py new file mode 100755 index 00000000000..136b4dc044b --- /dev/null +++ b/.github/scripts/get_affected.py @@ -0,0 +1,1027 @@ +""" +Affected Files Scanner +====================== + +Purpose +------- +Given a list of changed files, determine which Arduino sketches (.ino) or IDF component examples are affected +and should be rebuilt. + +Supports two modes: +- **Default mode**: Analyzes Arduino sketches (.ino files) +- **Component mode** (--component): Analyzes IDF component examples (directories) + +High-level workflow +------------------- +1) Discover include roots + - `include_folders` starts with `cores/esp32` and is extended with every `libraries/*/src` directory. + +2) Build forward dependency graph (`dependencies`) + - **Default mode**: Enumerate source files under `cores/esp32` and `libraries`. + - **Component mode**: Also includes files under `idf_component_examples`. + - For each file, parse `#include` lines and resolve headers: + a) First relative to the including file's directory. + b) Then under each folder in `include_folders`. + - Each `.ino` automatically depends on `cores/esp32/Arduino.h`. + - If a header with the same basename has an adjacent `.c`/`.cpp`, add those as dependencies as well. + +3) Optional: augment with Universal Ctags symbol mapping + - If Universal Ctags is available, the script runs it recursively on `include_folders` with JSON output. + - It indexes C/C++ function-like symbols (functions and prototypes), capturing scope (namespaces/classes) and signature. + - Two maps are produced: + - `ctags_header_to_qnames`: header path -> set of qualified names declared in that header. + - `ctags_defs_by_qname`: qualified name -> set of implementation source files (.c/.cpp/...). + - During dependency graph construction, when a header is included, the script finds all qualified names declared + in that header and adds their implementation files as dependencies of the includer. This connects declarations in + headers to implementations even when filenames do not match, and correctly handles namespaces/classes/methods via + qualified names. + +4) Build reverse dependency graph (`reverse_dependencies`) + - For each edge A -> B in `dependencies`, add B -> A in `reverse_dependencies`. + +5) Determine affected files + - **Default mode**: If any file in `build_files` changed, append preset sketches to affected list. + Start from changed files and walk `reverse_dependencies` breadth-first. Any `.ino` reached is marked as affected. + - **Component mode**: If any file in `idf_build_files` changed, all IDF component examples are affected. + If any file in `idf_project_files` changed, only that specific project is affected. + Walk dependencies to find component example directories containing affected files. + +6) Save artifacts + - `dependencies.json` (forward graph) + - `dependencies_reverse.json` (reverse graph) + - `ctags_tags.jsonl` (raw JSONL tag stream) + - `ctags_header_to_qnames.json` (header path -> set of qualified names declared in that header) + - `ctags_defs_by_qname.json` (qualified name -> set of implementation source files (.c/.cpp/...)) + +7) Set CI output + - If the script is running in a CI environment, set the following environment variables: + - `recompile_preset`: Whether the preset sketches should be recompiled + - `should_build`: Whether to build the sketches or component examples + - `chunks`: A list of chunk indices to build (maximum is defined by `MAX_CHUNKS`) + - `chunk_count`: The number of chunks to build (maximum is defined by `MAX_CHUNKS`) + +Build file patterns +-------------------- +- **build_files**: Core Arduino build system files (platform.txt, variants/**, etc.) +- **sketch_build_files**: Sketch-specific files (ci.yml, *.csv in example directories) +- **idf_build_files**: Core IDF build system files (CMakeLists.txt, idf_component.yml, etc.) +- **idf_project_files**: Project-specific IDF files (per-example CMakeLists.txt, sdkconfig, etc.) + +Notes on accuracy and limitations +--------------------------------- +- Header-only or template-heavy code that defines functions inline in headers will not produce separate implementation + files. The header remains the dependency. +- If a declaration has no available definition, no implementation edge is added for it. +- Ctags does not require compilation but depends on static parsing. Exotic macro tricks may not be fully resolved. + +Usage +----- +python get_affected.py [changed_file2] ... [--component] [--debug] + +Options: + --component Analyze IDF component examples instead of Arduino sketches + --debug Enable debug messages and save debug artifacts to disk + +Output: +- **Default mode**: Prints affected sketches relative file paths to stdout +- **Component mode**: Prints affected IDF component examples relative directory paths to stdout +""" + +import sys +import argparse +from pathlib import Path +import os +import re +import queue +import json +import subprocess +import shutil +import fnmatch + +script_dir = Path(__file__).resolve().parent +project_root = (script_dir / "../../").resolve() + +is_ci = os.environ.get("CI", "false").lower() == "true" +is_pr = os.environ.get("IS_PR", "true").lower() == "true" +max_chunks = int(os.environ.get("MAX_CHUNKS", "15")) +chunk_count = 0 + +# Whether to analyze component examples instead of sketches. +component_mode = False + +# A list of files that are used by the CI and build system. +# If any of these files change, the preset sketches should be recompiled. +build_files = [ + "package.json", + "tools/get.py", + "tools/get.exe", + "platform.txt", + "programmers.txt", + ".github/workflows/push.yml", + ".github/scripts/on-push.sh", + ".github/scripts/sketch_utils.sh", + ".github/scripts/get_affected.py", + ".github/scripts/install-*", + "variants/esp32*/**", +] + +# Files that are used by the sketch build system. +# If any of these files change, the sketch should be recompiled. +sketch_build_files = [ + "libraries/*/examples/**/ci.yml", + "libraries/*/examples/**/*.csv", +] + +# Files that are used by the IDF build system. +# If any of these files change, the IDF examples should be recompiled. +idf_build_files = [ + "CMakeLists.txt", + "idf_component.yml", + "Kconfig.projbuild", + ".github/workflows/build_component.yml", + ".github/scripts/on-push-idf.sh", + ".github/scripts/sketch_utils.sh", + ".github/scripts/get_affected.py", + ".github/scripts/check-cmakelists.sh", + "variants/esp32*/**", +] + +# Files that are used by the IDF examples. +# If any of these files change, the example that uses them should be recompiled. +idf_project_files = [ + "idf_component_examples/*/CMakeLists.txt", + "idf_component_examples/*/ci.yml", + "idf_component_examples/*/*.csv", + "idf_component_examples/*/sdkconfig*", + "idf_component_examples/*/main/*", +] + +# Preset sketches that are used to test build system changes. +preset_sketch_files = [ + "libraries/NetworkClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino", + "libraries/BLE/examples/Server/Server.ino", + "libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino", + "libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino", +] + +# Whether to recompile the preset sketches for all platforms. +recompile_preset = 0 + +source_folders = ["cores/esp32", "libraries"] +include_folders = ["cores/esp32"] # This will later be extended to include all library folders. + +# A dictionary that maps a file to the files that it depends on. +dependencies = {} + +# A dictionary that maps a file to the files that depend on it (reverse dependencies). +reverse_dependencies = {} + +# A list of sketches that are affected by the changes. +affected_sketches = [] + +# A regular expression to match include statements in source files. +include_regex = re.compile(r'#include\s*[<"]([^">]+)[">]') + +# Ctags-based symbol index (optional) +ctags_executable = None +ctags_available = False +ctags_header_to_qnames: dict[str, set[str]] = {} +ctags_defs_by_qname: dict[str, set[str]] = {} +header_extensions = {".h", ".hpp", ".hh", ".hxx"} +source_extensions = {".c", ".cc", ".cpp", ".cxx", ".ino"} +skip_extensions = {".md"} + +def is_header_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in header_extensions + +def is_source_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in source_extensions + +def should_skip_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in skip_extensions + +def detect_universal_ctags() -> bool: + """ + Return True if Universal Ctags is available on PATH. + """ + exec_names = ["ctags-universal", "ctags"] + + global ctags_executable + for exec_name in exec_names: + ctags_path = shutil.which(exec_name) + if ctags_path: + break + if not ctags_path: + return False + + ctags_executable = ctags_path + + try: + out = subprocess.run([ctags_executable, "--version"], capture_output=True, text=True, check=False) + return "Universal Ctags" in (out.stdout + out.stderr) + except Exception: + return False + +def normalize_function_signature(signature: str) -> str: + """ + Normalize a function signature by removing parameter names, keeping only types. + + This handles cases where header declarations and implementations have different parameter names. + Uses a simple heuristic: the last word in each parameter is typically the parameter name. + + For example: + - "ltoa(long val, char *s, int radix)" -> "ltoa(long,char *,int)" + - "ltoa(long value, char *result, int base)" -> "ltoa(long,char *,int)" + + Args: + signature: The function signature string, e.g., "(long val, char *s, int radix)" + + Returns: + Normalized signature with parameter names removed, e.g., "(long,char *,int)" + """ + if not signature: + return signature + + # Normalize signatures: treat (void) and () as equivalent (both mean no parameters) + if signature == "(void)": + return "()" + + if not (signature.startswith("(") and signature.endswith(")")): + return signature + + # Handle const qualifier at the end (e.g., "(int i) const") + const_qualifier = "" + if signature.endswith(" const"): + signature = signature[:-6] # Remove " const" + const_qualifier = " const" + + # Extract parameter list without parentheses + param_list = signature[1:-1] + if not param_list.strip(): + return "()" + const_qualifier + + # Split by comma and process each parameter + params = [] + for param in param_list.split(","): + param = param.strip() + if not param: + continue + + # Handle default parameters (e.g., "int x = 5") + if "=" in param: + param = param.split("=")[0].strip() + + # Try simple approach first: remove the last word + # This works for most cases: "int x" -> "int", "MyStruct s" -> "MyStruct" + import re + + # Handle arrays first: "int arr[10]" -> "int [10]", "char *argv[]" -> "char *[]" + array_match = re.match(r'^(.+?)\s+(\w+)((?:\[\d*\])+)$', param) + if array_match: + type_part = array_match.group(1).strip() + array_brackets = array_match.group(3) + params.append(type_part + array_brackets) + else: + # Handle function pointers: "int (*func)(int, char)" -> "int (*)(int, char)" + func_ptr_match = re.match(r'^(.+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\((.+?)\)\s*$', param) + if func_ptr_match: + return_type = func_ptr_match.group(1).strip() + inner_params = func_ptr_match.group(3).strip() + # Recursively normalize the inner parameters + if inner_params: + inner_normalized = normalize_function_signature(f"({inner_params})") + inner_normalized = inner_normalized[1:-1] # Remove outer parentheses + else: + inner_normalized = "" + params.append(f"{return_type} (*)({inner_normalized})") + else: + # Try simple approach: remove the last word + simple_match = re.match(r'^(.+)\s+(\w+)$', param) + if simple_match: + # Simple case worked - just remove the last word + type_part = simple_match.group(1).strip() + params.append(type_part) + else: + # Fallback to complex regex for edge cases with pointers + # First, try to match cases with pointers/references (including multiple *) + # Pattern: (everything before) (one or more * or &) (space) (parameter name) + m = re.match(r'^(.+?)(\s*[*&]+\s+)(\w+)$', param) + if m: + # Keep everything before the pointers, plus the pointers (without the space before param name) + type_part = m.group(1) + m.group(2).rstrip() + params.append(type_part.strip()) + else: + # Try to match cases without space between type and pointer: "char*ptr", "char**ptr" + m = re.match(r'^(.+?)([*&]+)(\w+)$', param) + if m: + # Keep everything before the pointers, plus the pointers + type_part = m.group(1) + m.group(2) + params.append(type_part.strip()) + else: + # Single word - assume it's a type + params.append(param.strip()) + + # Normalize spacing around pointers to ensure consistent output + # This ensures "char *" and "char*" both become "char *" + if params: + last_param = params[-1] + # Normalize spacing around * and & symbols + # Replace multiple spaces with single space, ensure space before * and & + normalized = re.sub(r'\s+', ' ', last_param) # Collapse multiple spaces + normalized = re.sub(r'\s*([*&]+)', r' \1', normalized) # Ensure space before * and & + normalized = re.sub(r'([*&]+)\s*', r'\1 ', normalized) # Ensure space after * and & + normalized = re.sub(r'\s+', ' ', normalized).strip() # Clean up extra spaces + params[-1] = normalized + + result = "(" + ",".join(params) + ")" + if const_qualifier: + result += const_qualifier + + return result + +def build_qname_from_tag(tag: dict) -> str: + """ + Compose a qualified name for a function/method using scope + name + signature. + Falls back gracefully if some fields are missing. + """ + scope = tag.get("scope") or "" + name = tag.get("name") or "" + signature = tag.get("signature") or "" + + # Normalize the signature to remove parameter names + signature = normalize_function_signature(signature) + + qparts = [] + if scope: + qparts.append(scope) + qparts.append(name) + qname = "::".join([p for p in qparts if p]) + return f"{qname}{signature}" + +def find_impl_files_for_qname(qname: str, defs_by_qname: dict[str, set[str]], header_path: str = None) -> set[str]: + """ + Find implementation files for a qualified name, handling namespace mismatches. + + Ctags may capture different namespace scopes in headers vs implementations. + For example: + - Header: fs::SDFS::begin(...) + - Implementation: SDFS::begin(...) + + This happens when implementations use "using namespace" directives. + + Strategy: + 1. Try exact match first + 2. If no match and qname has namespaces, try stripping ONLY outer namespace prefixes + (keep at least Class::method structure intact) + 3. If header_path provided, prefer implementations from same directory + """ + # Try exact match first + impl_files = defs_by_qname.get(qname, set()) + if impl_files: + return impl_files + + # If no exact match and the qname contains namespaces (::), try stripping them + if "::" in qname: + parts = qname.split("::") + # Only strip outer namespaces, not the class/method structure + # For "ns1::ns2::Class::method(...)", we want to try: + # - "ns2::Class::method(...)" (strip 1 level) + # - "Class::method(...)" (strip 2 levels) + # But NOT "method(...)" alone (too ambiguous) + + # Only allow stripping if we have more than 2 parts (namespace::Class::method) + # If we only have 2 parts (Class::method), don't strip as it would leave just "method" + if len(parts) > 2: + # Keep at least 2 parts (Class::method) to avoid false positives + max_strip = len(parts) - 2 + + for i in range(1, max_strip + 1): + shorter_qname = "::".join(parts[i:]) + impl_files = defs_by_qname.get(shorter_qname, set()) + + if impl_files: + # If we have the header path, prefer implementations from same directory + if header_path: + header_dir = os.path.dirname(header_path) + same_dir_files = {f for f in impl_files if os.path.dirname(f) == header_dir} + if same_dir_files: + return same_dir_files + + return impl_files + + return set() + +def run_ctags_and_index(paths: list[str]) -> tuple[dict[str, set[str]], dict[str, set[str]], str]: + """ + Run Universal Ctags over given paths (relative to project_root) and build: + - header_to_qnames: header path -> set of qualified names declared in that header + - defs_by_qname: qualified name -> set of source files where it is defined + Paths in results are relative to project_root to match this script's expectations. + """ + cmd = [ + ctags_executable, "-R", "-f", "-", + "--map-C++=+.ino", + "--languages=C,C++", + "--kinds-C=+p", + "--kinds-C++=+pf", + "--fields=+nKSt+s", + "--output-format=json", + ] + paths + + try: + proc = subprocess.run( + cmd, + cwd=project_root, + capture_output=True, + text=True, + check=True, + ) + print(f"Ctags completed successfully. Output length: {len(proc.stdout)}", file=sys.stderr) + except subprocess.CalledProcessError as e: + print(f"Error: Ctags failed with return code {e.returncode}", file=sys.stderr) + print(f"Ctags stdout: {e.stdout}", file=sys.stderr) + print(f"Ctags stderr: {e.stderr}", file=sys.stderr) + return {}, {}, "" + except Exception as e: + print(f"Warning: Failed to run ctags: {e}", file=sys.stderr) + return {}, {}, "" + + header_to_qnames: dict[str, set[str]] = {} + defs_by_qname: dict[str, set[str]] = {} + + for line in proc.stdout.splitlines(): + line = line.strip() + if not line or not line.startswith("{"): + continue + try: + tag = json.loads(line) + except Exception: + continue + + path = tag.get("path") + if not path: + continue + + # Only consider C/C++ function-like symbols + kind = tag.get("kind") + # Accept both long names and single-letter kinds from ctags + is_function_like = kind in ("function", "prototype") or kind in ("f", "p") + if not is_function_like: + continue + + qname = build_qname_from_tag(tag) + if not qname: + continue + + if is_header_file(path): + header_to_qnames.setdefault(path, set()).add(qname) + elif is_source_file(path): + defs_by_qname.setdefault(qname, set()).add(path) + + print(f"Parsed {len(header_to_qnames)} headers and {len(defs_by_qname)} symbol definitions", file=sys.stderr) + return header_to_qnames, defs_by_qname, proc.stdout + +def save_ctags_artifacts(raw_jsonl: str, header_to_qnames: dict[str, set[str]], defs_by_qname: dict[str, set[str]]) -> None: + """ + Save ctags artifacts to disk: + - Raw JSONL tags stream (ctags_tags.jsonl) + - Header to qualified names map (ctags_header_to_qnames.json) + - Qualified name to implementation files map (ctags_defs_by_qname.json) + """ + try: + with open("ctags_tags.jsonl", "w", encoding="utf-8") as f: + f.write(raw_jsonl) + # Convert sets to sorted lists for JSON serialization + hdr_json = {k: sorted(list(v)) for k, v in header_to_qnames.items()} + defs_json = {k: sorted(list(v)) for k, v in defs_by_qname.items()} + with open("ctags_header_to_qnames.json", "w", encoding="utf-8") as f: + json.dump(hdr_json, f, indent=2, sort_keys=True) + with open("ctags_defs_by_qname.json", "w", encoding="utf-8") as f: + json.dump(defs_json, f, indent=2, sort_keys=True) + print("Ctags artifacts saved: ctags_tags.jsonl, ctags_header_to_qnames.json, ctags_defs_by_qname.json", file=sys.stderr) + except Exception as e: + print(f"Warning: Failed to save ctags artifacts: {e}", file=sys.stderr) + +def find_library_folders() -> list[str]: + """ + Find all library src folders and append them to include_folders. + """ + libraries_path = project_root / "libraries" + if libraries_path.exists(): + for lib_dir in libraries_path.iterdir(): + if lib_dir.is_dir(): + src_path = lib_dir / "src" + if src_path.is_dir(): + include_folders.append(str(src_path.relative_to(project_root))) + +def list_files() -> list[str]: + """ + List all source files in the project as relative paths to project_root. + Only search in specific folders to avoid processing the entire project. + """ + files = [] + for folder in source_folders: + folder_path = os.path.join(project_root, folder) + if os.path.exists(folder_path): + for root, dirs, file_list in os.walk(folder_path): + for file in file_list: + if is_source_file(file) or is_header_file(file): + abs_path = os.path.join(root, file) + rel_path = os.path.relpath(abs_path, project_root) + files.append(str(rel_path)) + return files + +def list_ino_files() -> list[str]: + """ + List all .ino files in the project. + """ + files = [] + for file in list_files(): + if file.endswith('.ino'): + files.append(file) + return files + +def list_idf_component_examples() -> list[str]: + """ + List all IDF component example directories in the project. + """ + examples = [] + examples_path = project_root / "idf_component_examples" + if examples_path.exists(): + for example_dir in examples_path.iterdir(): + if example_dir.is_dir(): + examples.append(str(example_dir.relative_to(project_root))) + return examples + +def search_file(filepath: str, folders: list[str]) -> str: + """ + Search the filepath using the folders as root folders. + If the file is found, return the full path, otherwise return None. + """ + for folder in folders: + full_path = os.path.join(folder, filepath) + if os.path.exists(full_path): + return full_path + return None + +def resolve_include_path(include_file: str, current_file: str) -> str: + """ + Resolve an include statement to a full file path. + """ + + # First try relative to the current file's directory + current_dir = os.path.dirname(current_file) + if current_dir: + relative_path = os.path.join(current_dir, include_file) + if os.path.exists(os.path.join(project_root, relative_path)): + return relative_path + + # Then try to find the file in the include folders + resolved_path = search_file(include_file, include_folders) + if resolved_path: + return os.path.relpath(resolved_path, project_root) + + return None + +def build_dependencies_graph() -> None: + """ + Build a dependency graph for the project. + """ + q = queue.Queue() + for file in list_ino_files(): + q.put(file) + for file in list_files(): + dependencies[file] = [] + + processed_files = set() + + while not q.empty(): + file = q.get() + if file in processed_files: + continue + + processed_files.add(file) + file_path = os.path.join(project_root, file) + + # Automatically add Arduino.h dependency for .ino files + if file.endswith('.ino'): + dependencies[file].append('cores/esp32/Arduino.h') + + try: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + except Exception as e: + print(f"Warning: Could not read file {file}: {e}", file=sys.stderr) + continue + + for match in include_regex.finditer(content): + include_file = match.group(1) + if include_file.endswith('.h'): + header_path = resolve_include_path(include_file, file) + + # If the header file is found + if header_path: + # Add header file to dependencies + if header_path not in dependencies[file]: + dependencies[file].append(header_path) + # Only add to queue if it's a source file we haven't processed yet + if header_path in dependencies and header_path not in processed_files: + q.put(header_path) + + # Try to use ctags to match header declarations to implementation files + if ctags_available: + # Get qualified names of functions and methods declared in the header + qnames = ctags_header_to_qnames.get(header_path) + if qnames: + impl_files = set() + for qn in qnames: + # For each qualified name, find implementation files + # This handles namespace mismatches (e.g., fs::SDFS vs SDFS) + # Pass header_path to prefer implementations from same directory + impl_files |= find_impl_files_for_qname(qn, ctags_defs_by_qname, header_path) + for impl in impl_files: + # Skip .ino files - they should never be dependencies of other files + if impl.endswith('.ino'): + continue + # Add implementation file to dependencies + if impl not in dependencies[file]: + dependencies[file].append(impl) + if impl in dependencies and impl not in processed_files: + q.put(impl) + else: + # Fall back to resolving .cpp and .c files + cpp_path = resolve_include_path(include_file.replace('.h', '.cpp'), file) + c_path = resolve_include_path(include_file.replace('.h', '.c'), file) + + # Add C++ file to dependencies if it exists + if cpp_path and cpp_path not in dependencies[file]: + dependencies[file].append(cpp_path) + if cpp_path in dependencies and cpp_path not in processed_files: + q.put(cpp_path) + + # Add C file to dependencies if it exists + if c_path and c_path not in dependencies[file]: + dependencies[file].append(c_path) + if c_path in dependencies and c_path not in processed_files: + q.put(c_path) + + +def build_reverse_dependencies() -> None: + """ + Build a reverse dependency graph showing which files depend on each file. + """ + for file, deps in dependencies.items(): + for dependency in deps: + if dependency not in reverse_dependencies: + reverse_dependencies[dependency] = [] + reverse_dependencies[dependency].append(file) + +def has_build_files_changed(changed_files: list[str]) -> bool: + """ + Check if any of the build files have changed. + Supports glob patterns in build_files. + """ + for file in changed_files: + if should_skip_file(file): + continue + + if not component_mode: + for pattern in build_files: + if fnmatch.fnmatch(file, pattern): + return True + else: + for pattern in idf_build_files: + if fnmatch.fnmatch(file, pattern): + return True + + return False + +def has_sketch_build_files_changed(path: str) -> bool: + """ + Check if any of the sketch build files have changed. + """ + if should_skip_file(path): + return False + + if not component_mode: + for pattern in sketch_build_files: + if fnmatch.fnmatch(path, pattern): + return True + else: + for pattern in idf_project_files: + if fnmatch.fnmatch(path, pattern): + return True + return False + +def preprocess_changed_files(changed_files: list[str]) -> None: + """ + Preprocess the changed files to detect build system changes. + """ + # Special case: if the Arduino libs or tools changes, rebuild all sketches on PRs + if is_pr and not component_mode: + for changed in changed_files: + if changed == "package/package_esp32_index.template.json": + print("Package index changed - all sketches affected", file=sys.stderr) + all_sketches = list_ino_files() + for sketch in all_sketches: + if sketch not in affected_sketches: + affected_sketches.append(sketch) + # No need to continue preprocessing; dependency walk will still run afterwards + break + # If not a PR, skip preprocessing as we'll recompile everything anyway + if not is_pr: + return + + # Check if any build files have changed + if has_build_files_changed(changed_files): + if component_mode: + # If IDF build files changed, build all IDF component examples + print("IDF build files changed - all component examples affected", file=sys.stderr) + for example in list_idf_component_examples(): + if example not in affected_sketches: + affected_sketches.append(example) + else: + # If build files changed, build the preset sketches + print("Build files changed - preset sketches affected", file=sys.stderr) + global recompile_preset + recompile_preset = 1 + for file in preset_sketch_files: + if file not in affected_sketches: + affected_sketches.append(file) + + for file in changed_files: + if has_sketch_build_files_changed(file): + if component_mode: + # Extract the project name from idf_project_files pattern + # e.g., "idf_component_examples/hello_world/CMakeLists.txt" -> "idf_component_examples/hello_world" + if file.startswith("idf_component_examples/"): + parts = file.split("/") + if len(parts) >= 2: + project_path = "/".join(parts[:2]) # idf_component_examples/project_name + if project_path not in affected_sketches: + print(f"IDF project file changed - {project_path} affected", file=sys.stderr) + affected_sketches.append(project_path) + else: + dir_name = os.path.dirname(file) + sketch_name = dir_name + "/" + os.path.basename(dir_name) + ".ino" + if sketch_name not in affected_sketches: + affected_sketches.append(sketch_name) + +def find_affected_sketches(changed_files: list[str]) -> None: + """ + Find the sketches that are affected by the changes. + """ + + # If not a PR, recompile everything + if not is_pr: + if component_mode: + print("Not a PR - recompiling all IDF component examples", file=sys.stderr) + all_examples = list_idf_component_examples() + for example in all_examples: + if example not in affected_sketches: + affected_sketches.append(example) + print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr) + else: + print("Not a PR - recompiling all sketches", file=sys.stderr) + all_sketches = list_ino_files() + for sketch in all_sketches: + if sketch not in affected_sketches: + affected_sketches.append(sketch) + print(f"Total affected sketches: {len(affected_sketches)}", file=sys.stderr) + return + + # For component mode: if any *source code* file (not example or documentation) changed, recompile all examples + if component_mode: + for file in changed_files: + if (is_source_file(file) or is_header_file(file)) and not file.endswith(".ino"): + if file.startswith("cores/") or file.startswith("libraries/"): + print("Component mode: file changed in cores/ or libraries/ - recompiling all IDF component examples", file=sys.stderr) + all_examples = list_idf_component_examples() + for example in all_examples: + if example not in affected_sketches: + affected_sketches.append(example) + print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr) + return + + preprocess_changed_files(changed_files) + + # Normal dependency-based analysis for non-critical changes + processed_files = set() + q = queue.Queue() + + if component_mode: + # Get all available component examples once for efficiency + all_examples = list_idf_component_examples() + else: + all_examples = [] + + for file in changed_files: + if not should_skip_file(file): + q.put(file) + + while not q.empty(): + file = q.get() + if file in processed_files: + continue + + processed_files.add(file) + + if component_mode: + # Check if this file belongs to an IDF component example + for example in all_examples: + if file.startswith(example + "/") and example not in affected_sketches: + affected_sketches.append(example) + else: + if file.endswith('.ino') and file not in affected_sketches: + affected_sketches.append(file) + + # Continue with reverse dependency traversal + if file in reverse_dependencies: + for dependency in reverse_dependencies[file]: + if dependency not in processed_files: + if component_mode: + # In component mode, only follow dependencies within the same example + # IDF component examples are isolated - no cross-example dependencies + current_example = None + dependency_example = None + + # Find which examples the current file and dependency belong to + for example in all_examples: + if file.startswith(example + "/"): + current_example = example + if dependency.startswith(example + "/"): + dependency_example = example + + # Traverse dependencies based on context: + # 1. If current file is NOT in any example (shared/core file), allow traversal to any example + # 2. If current file IS in an example, only traverse within the same example + should_traverse = False + + if not current_example: + # Current file is a shared/core file - can affect any example + should_traverse = True + elif current_example == dependency_example: + # Both files are in the same component example + should_traverse = True + + if should_traverse: + q.put(dependency) + if dependency_example and dependency_example not in affected_sketches: + affected_sketches.append(dependency_example) + else: + q.put(dependency) + if dependency.endswith('.ino') and dependency not in affected_sketches: + affected_sketches.append(dependency) + + if component_mode: + print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr) + if affected_sketches: + print("Affected IDF component examples:", file=sys.stderr) + for example in affected_sketches: + print(f" {example}", file=sys.stderr) + else: + print(f"Total affected sketches: {len(affected_sketches)}", file=sys.stderr) + if affected_sketches: + print("Affected sketches:", file=sys.stderr) + for sketch in affected_sketches: + print(f" {sketch}", file=sys.stderr) + +def save_dependencies_as_json(output_file: str = "dependencies.json") -> None: + """ + Save both the forward and reverse dependency graphs as JSON files. + """ + # Save forward dependencies + forward_deps_file = output_file + with open(forward_deps_file, 'w') as f: + json.dump(dependencies, f, indent=2, sort_keys=True) + print(f"Forward dependencies saved to: {forward_deps_file}", file=sys.stderr) + + # Save reverse dependencies + reverse_deps_file = output_file.replace('.json', '_reverse.json') + with open(reverse_deps_file, 'w') as f: + json.dump(reverse_dependencies, f, indent=2, sort_keys=True) + print(f"Reverse dependencies saved to: {reverse_deps_file}", file=sys.stderr) + +def check_preset_files_affected(): + """ + Check if any of the preset sketch files are in the affected sketches list. + If so, set recompile_preset to True. + """ + global recompile_preset + + # If not a PR, always recompile preset sketches + if not is_pr: + if not component_mode: # Only check preset files in sketch mode + print("Not a PR - setting recompile_preset=1 for all preset sketches", file=sys.stderr) + recompile_preset = 1 + return + + if not component_mode: # Only check preset files in sketch mode + for preset_file in preset_sketch_files: + if preset_file in affected_sketches: + print(f"Preset sketches affected - setting recompile_preset=1", file=sys.stderr) + recompile_preset = 1 + break + +def set_ci_output(print_vars: bool=True): + """ + Set GITHUB_OUTPUT environment variable + """ + # Determine output target + if not is_ci and print_vars: + print("Not a CI environment - printing variables to stdout:", file=sys.stderr) + f = sys.stderr + elif is_ci: + out = os.environ["GITHUB_OUTPUT"] + f = open(out, "a") + else: + return + + try: + # Write all output values + f.write(f"recompile_preset={recompile_preset}\n") + chunk_count = len(affected_sketches) + if chunk_count > 0: + f.write(f"should_build=1\n") + if chunk_count > max_chunks: + print(f"More sketches than the allowed number of chunks found. Limiting to {max_chunks} chunks.", file=sys.stderr) + chunk_count = max_chunks + chunks='["0"' + for i in range(1, chunk_count): + chunks+=f",\"{i}\"" + chunks+="]" + f.write(f"chunks={chunks}\n") + else: + f.write(f"should_build=0\n") + f.write(f"chunk_count={chunk_count}\n") + finally: + # Close file if we opened it (CI mode) + if is_ci: + f.close() + +def main(): + parser = argparse.ArgumentParser(description="Affected Sketches Scanner") + parser.add_argument("changed_files", nargs="+", help="List of changed files (e.g., file1.cpp file2.h file3.cpp)") + parser.add_argument("--component", action="store_true", help="Get affected component examples instead of sketches") + parser.add_argument("--debug", action="store_true", help="Enable debug messages and save debug artifacts to disk") + args = parser.parse_args() + + changed_files = args.changed_files + global component_mode + component_mode = args.component + if component_mode: + print(f"Analyzing IDF component examples...", file=sys.stderr) + source_folders.append("idf_component_examples") + else: + print(f"Analyzing sketches...", file=sys.stderr) + + print(f"Finding include folders...", file=sys.stderr) + find_library_folders() + print(f"Found {len(include_folders)} include folders", file=sys.stderr) + + # Initialize ctags-based symbol index if available + global ctags_available, ctags_header_to_qnames, ctags_defs_by_qname + ctags_available = detect_universal_ctags() + if ctags_available: + print("Universal Ctags detected - indexing symbols for header-to-implementation mapping...", file=sys.stderr) + try: + hdr_to_qn, defs_by_qn, raw_jsonl = run_ctags_and_index(source_folders) + ctags_header_to_qnames, ctags_defs_by_qname = hdr_to_qn, defs_by_qn + print(f"Ctags index built: {len(ctags_header_to_qnames)} headers, {len(ctags_defs_by_qname)} symbols", file=sys.stderr) + if args.debug: + print("Saving ctags artifacts...", file=sys.stderr) + save_ctags_artifacts(raw_jsonl, ctags_header_to_qnames, ctags_defs_by_qname) + except Exception as e: + print(f"Warning: Ctags indexing failed: {e}", file=sys.stderr) + ctags_available = False + else: + print("Universal Ctags not found - proceeding without symbol-based mapping", file=sys.stderr) + + print(f"Building dependency graph...", file=sys.stderr) + build_dependencies_graph() + print(f"Processed {len(dependencies)} files", file=sys.stderr) + + print(f"Building reverse dependency graph...", file=sys.stderr) + build_reverse_dependencies() + + # Save dependencies as JSON artifacts + if args.debug: + print(f"Saving dependencies as JSON artifacts...", file=sys.stderr) + save_dependencies_as_json() + + print(f"Finding affected sketches...", file=sys.stderr) + if not is_pr: + print("Not a PR - will recompile everything", file=sys.stderr) + find_affected_sketches(changed_files) + + print(f"Checking if preset sketch files were affected...", file=sys.stderr) + check_preset_files_affected() + + print(f"Printing affected sketches...", file=sys.stderr) + for sketch in affected_sketches: + print(sketch) + + set_ci_output(args.debug) + +if __name__ == "__main__": + main() diff --git a/.github/scripts/merge_packages.py b/.github/scripts/merge_packages.py index 8d1f200ec5c..3ca781678e1 100755 --- a/.github/scripts/merge_packages.py +++ b/.github/scripts/merge_packages.py @@ -17,7 +17,8 @@ def load_package(filename): - pkg = json.load(open(filename))["packages"][0] + with open(filename) as f: + pkg = json.load(f)["packages"][0] print("Loaded package {0} from {1}".format(pkg["name"], filename), file=sys.stderr) print("{0} platform(s), {1} tools".format(len(pkg["platforms"]), len(pkg["tools"])), file=sys.stderr) return pkg diff --git a/.github/scripts/on-push-idf.sh b/.github/scripts/on-push-idf.sh index 72e7c7f574e..66a61b2dff6 100644 --- a/.github/scripts/on-push-idf.sh +++ b/.github/scripts/on-push-idf.sh @@ -7,27 +7,33 @@ CHECK_REQUIREMENTS="./components/arduino-esp32/.github/scripts/sketch_utils.sh c # Export IDF environment . ${IDF_PATH}/export.sh -# Find all examples in ./components/arduino-esp32/idf_component_examples -idf_component_examples=$(find ./components/arduino-esp32/idf_component_examples -mindepth 1 -maxdepth 1 -type d) - -for example in $idf_component_examples; do - if [ -f "$example"/ci.json ]; then +if [ -n "$1" ]; then + # If a file is provided, use it to get the affected examples. + affected_examples=$(cat "$1" | sort) +else + # Otherwise, find all examples in ./components/arduino-esp32/idf_component_examples + affected_examples=$(find ./components/arduino-esp32/idf_component_examples -mindepth 1 -maxdepth 1 -type d | sed 's|^\./components/arduino-esp32/||' | sort) +fi + +for example in $affected_examples; do + example_path="$PWD/components/arduino-esp32/$example" + if [ -f "$example_path/ci.yml" ]; then # If the target is listed as false, skip the sketch. Otherwise, include it. - is_target=$(jq -r --arg target "$IDF_TARGET" '.targets[$target]' "$example"/ci.json) + is_target=$(yq eval ".targets.${IDF_TARGET}" "$example_path/ci.yml" 2>/dev/null) if [[ "$is_target" == "false" ]]; then printf "\n\033[93mSkipping %s for target %s\033[0m\n\n" "$example" "$IDF_TARGET" continue fi fi - idf.py -C "$example" set-target "$IDF_TARGET" + idf.py --preview -C "$example_path" set-target "$IDF_TARGET" - has_requirements=$(${CHECK_REQUIREMENTS} "$example" "$example/sdkconfig") + has_requirements=$(${CHECK_REQUIREMENTS} "$example_path" "$example_path/sdkconfig") if [ "$has_requirements" -eq 0 ]; then printf "\n\033[93m%s does not meet the requirements for %s. Skipping...\033[0m\n\n" "$example" "$IDF_TARGET" continue fi printf "\n\033[95mBuilding %s\033[0m\n\n" "$example" - idf.py -C "$example" -DEXTRA_COMPONENT_DIRS="$PWD/components" build + idf.py --preview -C "$example_path" -DEXTRA_COMPONENT_DIRS="$PWD/components" build done diff --git a/.github/scripts/on-release.sh b/.github/scripts/on-release.sh index 275c74f8ea5..c4dadccc4f7 100755 --- a/.github/scripts/on-release.sh +++ b/.github/scripts/on-release.sh @@ -199,8 +199,31 @@ set -e ## mkdir -p "$OUTPUT_DIR" -PKG_DIR="$OUTPUT_DIR/$PACKAGE_NAME" +PKG_DIR="${OUTPUT_DIR:?}/$PACKAGE_NAME" PACKAGE_ZIP="$PACKAGE_NAME.zip" +PACKAGE_XZ="$PACKAGE_NAME.tar.xz" +LIBS_ZIP="$PACKAGE_NAME-libs.zip" +LIBS_XZ="$PACKAGE_NAME-libs.tar.xz" + +echo "Updating version..." +if ! "${SCRIPTS_DIR}/update-version.sh" "$RELEASE_TAG"; then + echo "ERROR: update_version failed!" + exit 1 +fi +git config --global github.user "github-actions[bot]" +git config --global user.name "github-actions[bot]" +git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" +git add . + +# We should only commit if there are changes +need_update_commit=true +if git diff --cached --quiet; then + echo "Version already updated" + need_update_commit=false +else + echo "Creating version update commit..." + git commit -m "change(version): Update core version to $RELEASE_TAG" +fi echo "Updating submodules ..." git -C "$GITHUB_WORKSPACE" submodule update --init --recursive > /dev/null 2>&1 @@ -283,7 +306,7 @@ echo \#define ARDUINO_ESP32_GIT_DESC "$(git -C "$GITHUB_WORKSPACE" describe --ta echo \#define ARDUINO_ESP32_RELEASE_"$ver_define" >> "$PKG_DIR/cores/esp32/core_version.h" echo \#define ARDUINO_ESP32_RELEASE \""$ver_define"\" >> "$PKG_DIR/cores/esp32/core_version.h" -# Compress package folder +# Compress ZIP package folder echo "Creating ZIP ..." pushd "$OUTPUT_DIR" >/dev/null zip -qr "$PACKAGE_ZIP" "$PACKAGE_NAME" @@ -293,22 +316,118 @@ if [ $? -ne 0 ]; then fi # Calculate SHA-256 -echo "Calculating SHA sum ..." -PACKAGE_PATH="$OUTPUT_DIR/$PACKAGE_ZIP" +echo "Calculating ZIP SHA sum ..." +PACKAGE_PATH="${OUTPUT_DIR:?}/$PACKAGE_ZIP" PACKAGE_SHA=$(shasum -a 256 "$PACKAGE_ZIP" | cut -f 1 -d ' ') PACKAGE_SIZE=$(get_file_size "$PACKAGE_ZIP") popd >/dev/null -rm -rf "$PKG_DIR" echo "'$PACKAGE_ZIP' Created! Size: $PACKAGE_SIZE, SHA-256: $PACKAGE_SHA" echo -# Upload package to release page -echo "Uploading package to release page ..." +# Compress XZ package folder +echo "Creating XZ ..." +pushd "$OUTPUT_DIR" >/dev/null +tar -cJf "$PACKAGE_XZ" "$PACKAGE_NAME" +if [ $? -ne 0 ]; then + echo "ERROR: Failed to create $PACKAGE_XZ ($?)" + exit 1 +fi + +# Calculate SHA-256 +echo "Calculating XZ SHA sum ..." +PACKAGE_XZ_PATH="${OUTPUT_DIR:?}/$PACKAGE_XZ" +PACKAGE_XZ_SHA=$(shasum -a 256 "$PACKAGE_XZ" | cut -f 1 -d ' ') +PACKAGE_XZ_SIZE=$(get_file_size "$PACKAGE_XZ") +popd >/dev/null +echo "'$PACKAGE_XZ' Created! Size: $PACKAGE_XZ_SIZE, SHA-256: $PACKAGE_XZ_SHA" +echo + +# Upload ZIP package to release page +echo "Uploading ZIP package to release page ..." PACKAGE_URL=$(git_safe_upload_asset "$PACKAGE_PATH") echo "Package Uploaded" echo "Download URL: $PACKAGE_URL" echo +# Upload XZ package to release page +echo "Uploading XZ package to release page ..." +PACKAGE_XZ_URL=$(git_safe_upload_asset "$PACKAGE_XZ_PATH") +echo "Package Uploaded" +echo "Download URL: $PACKAGE_XZ_URL" +echo + +# Remove package folder +rm -rf "$PKG_DIR" + +# Copy Libs from lib-builder to release in ZIP and XZ + +libs_url=$(cat "$PACKAGE_JSON_TEMPLATE" | jq -r ".packages[0].tools[] | select(.name == \"esp32-arduino-libs\") | .systems[0].url") +libs_sha=$(cat "$PACKAGE_JSON_TEMPLATE" | jq -r ".packages[0].tools[] | select(.name == \"esp32-arduino-libs\") | .systems[0].checksum" | sed 's/^SHA-256://') +libs_size=$(cat "$PACKAGE_JSON_TEMPLATE" | jq -r ".packages[0].tools[] | select(.name == \"esp32-arduino-libs\") | .systems[0].size") +echo "Downloading libs from lib-builder ..." +echo "URL: $libs_url" +echo "Expected SHA: $libs_sha" +echo "Expected Size: $libs_size" +echo + +echo "Downloading libs from lib-builder ..." +curl -L -o "$OUTPUT_DIR/$LIBS_ZIP" "$libs_url" + +# Check SHA and Size +zip_sha=$(sha256sum "$OUTPUT_DIR/$LIBS_ZIP" | awk '{print $1}') +zip_size=$(stat -c%s "$OUTPUT_DIR/$LIBS_ZIP") +echo "Downloaded SHA: $zip_sha" +echo "Downloaded Size: $zip_size" +if [ "$zip_sha" != "$libs_sha" ] || [ "$zip_size" != "$libs_size" ]; then + echo "ERROR: ZIP SHA and Size do not match" + exit 1 +fi + +# Extract ZIP + +echo "Repacking libs to XZ ..." +unzip -q "$OUTPUT_DIR/$LIBS_ZIP" -d "$OUTPUT_DIR" +pushd "$OUTPUT_DIR" >/dev/null +tar -cJf "$LIBS_XZ" "esp32-arduino-libs" +popd >/dev/null + +# Copy esp-hosted binaries + +mkdir -p "$GITHUB_WORKSPACE/hosted" +cp "$OUTPUT_DIR/esp32-arduino-libs/hosted"/*.bin "$GITHUB_WORKSPACE/hosted/" + +# Upload ZIP and XZ libs to release page + +echo "Uploading ZIP libs to release page ..." +LIBS_ZIP_URL=$(git_safe_upload_asset "$OUTPUT_DIR/$LIBS_ZIP") +echo "ZIP libs Uploaded" +echo "Download URL: $LIBS_ZIP_URL" +echo + +echo "Uploading XZ libs to release page ..." +LIBS_XZ_URL=$(git_safe_upload_asset "$OUTPUT_DIR/$LIBS_XZ") +echo "XZ libs Uploaded" +echo "Download URL: $LIBS_XZ_URL" +echo + +# Update libs URLs in JSON template +echo "Updating libs URLs in JSON template ..." + +# Update all libs URLs in the JSON template +libs_jq_arg="(.packages[0].tools[] | select(.name == \"esp32-arduino-libs\") | .systems[].url) = \"$LIBS_ZIP_URL\" |\ + (.packages[0].tools[] | select(.name == \"esp32-arduino-libs\") | .systems[].archiveFileName) = \"$LIBS_ZIP\"" + +cat "$PACKAGE_JSON_TEMPLATE" | jq "$libs_jq_arg" > "$OUTPUT_DIR/package-libs-updated.json" +PACKAGE_JSON_TEMPLATE="$OUTPUT_DIR/package-libs-updated.json" + +echo "Libs URLs updated in JSON template" +echo + +# Clean up +rm -rf "${OUTPUT_DIR:?}/esp32-arduino-libs" +rm -rf "${OUTPUT_DIR:?}/$LIBS_ZIP" +rm -rf "${OUTPUT_DIR:?}/$LIBS_XZ" + ## ## TEMP WORKAROUND FOR RV32 LONG PATH ON WINDOWS ## @@ -469,6 +588,17 @@ if [ "$RELEASE_PRE" == "false" ]; then echo fi +if [ "$need_update_commit" == "true" ]; then + echo "Pushing version update commit..." + git push + new_tag_commit=$(git rev-parse HEAD) + echo "New commit: $new_tag_commit" + + echo "Moving tag $RELEASE_TAG to $new_tag_commit..." + git tag -f "$RELEASE_TAG" "$new_tag_commit" + git push --force origin "$RELEASE_TAG" +fi + set +e ## diff --git a/.github/scripts/runtime_table_generator.py b/.github/scripts/runtime_table_generator.py new file mode 100644 index 00000000000..bf6c544f452 --- /dev/null +++ b/.github/scripts/runtime_table_generator.py @@ -0,0 +1,181 @@ +import json +import logging +import sys +import os +import re +from datetime import datetime + +# Configure logging +logging.basicConfig(level=logging.DEBUG, format='[%(levelname)s] %(message)s', stream=sys.stderr) + +SUCCESS_SYMBOL = ":white_check_mark:" +FAILURE_SYMBOL = ":x:" +ERROR_SYMBOL = ":fire:" + +# Load the JSON file passed as argument to the script +with open(sys.argv[1], "r") as f: + data = json.load(f) + tests = sorted(data["stats"]["suite_details"], key=lambda x: x["name"]) + +# Get commit SHA from command line argument or environment variable +commit_sha = None +if len(sys.argv) < 2 or len(sys.argv) > 3: + print(f"Usage: python {sys.argv[0]} [commit_sha]", file=sys.stderr) + sys.exit(1) +elif len(sys.argv) == 3: # Commit SHA is provided as argument + commit_sha = sys.argv[2] +elif "GITHUB_SHA" in os.environ: # Commit SHA is provided as environment variable + commit_sha = os.environ["GITHUB_SHA"] +else: # Commit SHA is not provided + print("Commit SHA is not provided. Please provide it as an argument or set the GITHUB_SHA environment variable.", file=sys.stderr) + sys.exit(1) + +# Generate the table + +print("## Runtime Test Results") +print("") + +try: + if os.environ["IS_FAILING"] == "true": + print(f"{FAILURE_SYMBOL} **The test workflows are failing. Please check the run logs.** {FAILURE_SYMBOL}") + print("") + else: + print(f"{SUCCESS_SYMBOL} **The test workflows are passing.** {SUCCESS_SYMBOL}") + print("") +except KeyError as e: + logging.debug(f"IS_FAILING environment variable not set: {e}") + +print("### Validation Tests") + +# Read platform-specific target lists from environment variables +# Map env var names to test suite platform names: hw->hardware, wokwi->wokwi, qemu->qemu +platform_targets = {} +try: + hw_targets = json.loads(os.environ.get("HW_TARGETS", "[]")) + wokwi_targets = json.loads(os.environ.get("WOKWI_TARGETS", "[]")) + qemu_targets = json.loads(os.environ.get("QEMU_TARGETS", "[]")) + + platform_targets["hardware"] = sorted(hw_targets) if hw_targets else [] + platform_targets["wokwi"] = sorted(wokwi_targets) if wokwi_targets else [] + platform_targets["qemu"] = sorted(qemu_targets) if qemu_targets else [] +except (json.JSONDecodeError, KeyError) as e: + print(f"Warning: Could not parse platform targets from environment: {e}", file=sys.stderr) + platform_targets = {"hardware": [], "wokwi": [], "qemu": []} + +proc_test_data = {} + +# Build executed tests map and collect targets +executed_tests_index = {} # {(platform, target, test_name): {tests, failures, errors}} +executed_run_counts = {} # {(platform, target, test_name): int} + +for test in tests: + if test["name"].startswith("performance_"): + continue + + try: + test_type, platform, target, rest = test["name"].split("_", 3) + except ValueError as e: + # Unexpected name, skip + test_name = test.get("name", "unknown") + logging.warning(f"Skipping test with unexpected name format '{test_name}': {e}") + continue + + # Remove an optional trailing numeric index (multi-FQBN builds) + m = re.match(r"(.+?)(\d+)?$", rest) + test_name = m.group(1) if m else rest + + if platform not in proc_test_data: + proc_test_data[platform] = {} + + if test_name not in proc_test_data[platform]: + proc_test_data[platform][test_name] = {} + + if target not in proc_test_data[platform][test_name]: + proc_test_data[platform][test_name][target] = { + "failures": 0, + "total": 0, + "errors": 0 + } + + proc_test_data[platform][test_name][target]["total"] += test["tests"] + proc_test_data[platform][test_name][target]["failures"] += test["failures"] + proc_test_data[platform][test_name][target]["errors"] += test["errors"] + + executed_tests_index[(platform, target, test_name)] = proc_test_data[platform][test_name][target] + executed_run_counts[(platform, target, test_name)] = executed_run_counts.get((platform, target, test_name), 0) + 1 + +# Render only executed tests grouped by platform/target/test +for platform in proc_test_data: + print("") + print(f"#### {platform.capitalize()}") + print("") + + # Get platform-specific target list + target_list = platform_targets.get(platform, []) + + if not target_list: + print(f"No targets configured for platform: {platform}") + continue + + print("Test", end="") + + for target in target_list: + # Make target name uppercase and add hyfen if not esp32 + display_target = target + if target != "esp32": + display_target = target.replace("esp32", "esp32-") + + print(f"|{display_target.upper()}", end="") + + print("") + print("-" + "|:-:" * len(target_list)) + + platform_executed = proc_test_data.get(platform, {}) + for test_name in sorted(platform_executed.keys()): + print(f"{test_name}", end="") + for target in target_list: + executed_cell = platform_executed.get(test_name, {}).get(target) + if executed_cell: + if executed_cell["errors"] > 0: + print(f"|Error {ERROR_SYMBOL}", end="") + else: + print(f"|{executed_cell['total']-executed_cell['failures']}/{executed_cell['total']}", end="") + if executed_cell["failures"] > 0: + print(f" {FAILURE_SYMBOL}", end="") + else: + print(f" {SUCCESS_SYMBOL}", end="") + else: + print("|-", end="") + print("") + +print("\n") +print(f"Generated on: {datetime.now().strftime('%Y/%m/%d %H:%M:%S')}") +print("") + +try: + repo = os.environ['GITHUB_REPOSITORY'] + commit_url = f"https://github.com/{repo}/commit/{commit_sha}" + build_workflow_url = f"https://github.com/{repo}/actions/runs/{os.environ['BUILD_RUN_ID']}" + wokwi_hw_workflow_url = f"https://github.com/{repo}/actions/runs/{os.environ['WOKWI_RUN_ID']}" + results_workflow_url = f"https://github.com/{repo}/actions/runs/{os.environ['RESULTS_RUN_ID']}" + results_url = os.environ['RESULTS_URL'] + print(f"[Commit]({commit_url}) / [Build and QEMU run]({build_workflow_url}) / [Hardware and Wokwi run]({wokwi_hw_workflow_url}) / [Results processing]({results_workflow_url})") + print("") + print(f"[Test results]({results_url})") +except KeyError as e: + logging.debug(f"Required environment variable for URL generation not set: {e}") + +# Save test results to JSON file +results_data = { + "commit_sha": commit_sha, + "tests_failed": os.environ["IS_FAILING"] == "true", + "test_data": proc_test_data, + "generated_at": datetime.now().isoformat() +} + +with open("test_results.json", "w") as f: + json.dump(results_data, f, indent=2) + +print(f"\nTest results saved to test_results.json", file=sys.stderr) +print(f"Commit SHA: {commit_sha}", file=sys.stderr) +print(f"Tests failed: {results_data['tests_failed']}", file=sys.stderr) diff --git a/.github/scripts/set_push_chunks.sh b/.github/scripts/set_push_chunks.sh deleted file mode 100644 index 69cd9a7f7de..00000000000 --- a/.github/scripts/set_push_chunks.sh +++ /dev/null @@ -1,83 +0,0 @@ -#!/bin/bash - -build_all=false -chunks_count=0 - -if [[ $CORE_CHANGED == 'true' ]] || [[ $IS_PR != 'true' ]]; then - echo "Core files changed or not a PR. Building all." - build_all=true - chunks_count=$MAX_CHUNKS -elif [[ $LIB_CHANGED == 'true' ]]; then - echo "Libraries changed. Building only affected sketches." - if [[ $NETWORKING_CHANGED == 'true' ]]; then - echo "Networking libraries changed. Building networking related sketches." - networking_sketches="$(find libraries/WiFi -name '*.ino') " - networking_sketches+="$(find libraries/Ethernet -name '*.ino') " - networking_sketches+="$(find libraries/PPP -name '*.ino') " - networking_sketches+="$(find libraries/NetworkClientSecure -name '*.ino') " - networking_sketches+="$(find libraries/WebServer -name '*.ino') " - fi - if [[ $FS_CHANGED == 'true' ]]; then - echo "FS libraries changed. Building FS related sketches." - fs_sketches="$(find libraries/SD -name '*.ino') " - fs_sketches+="$(find libraries/SD_MMC -name '*.ino') " - fs_sketches+="$(find libraries/SPIFFS -name '*.ino') " - fs_sketches+="$(find libraries/LittleFS -name '*.ino') " - fs_sketches+="$(find libraries/FFat -name '*.ino') " - fi - sketches="$networking_sketches $fs_sketches" - for file in $LIB_FILES; do - lib=$(echo "$file" | awk -F "/" '{print $1"/"$2}') - if [[ "$file" == *.ino ]]; then - # If file ends with .ino, add it to the list of sketches - echo "Sketch found: $file" - sketches+="$file " - elif [[ "$file" == "$lib/src/"* ]]; then - # If file is inside the src directory, find all sketches in the lib/examples directory - echo "Library src file found: $file" - if [[ -d $lib/examples ]]; then - lib_sketches=$(find "$lib"/examples -name '*.ino') - sketches+="$lib_sketches " - echo "Library sketches: $lib_sketches" - fi - else - # If file is in a example folder but it is not a sketch, find all sketches in the current directory - echo "File in example folder found: $file" - sketch=$(find "$(dirname "$file")" -name '*.ino') - sketches+="$sketch " - echo "Sketch in example folder: $sketch" - fi - echo "" - done -fi - -if [[ -n $sketches ]]; then - # Remove duplicates - sketches=$(echo "$sketches" | tr ' ' '\n' | sort | uniq) - for sketch in $sketches; do - echo "$sketch" >> sketches_found.txt - chunks_count=$((chunks_count+1)) - done - echo "Number of sketches found: $chunks_count" - echo "Sketches:" - echo "$sketches" - - if [[ $chunks_count -gt $MAX_CHUNKS ]]; then - echo "More sketches than the allowed number of chunks found. Limiting to $MAX_CHUNKS chunks." - chunks_count=$MAX_CHUNKS - fi -fi - -chunks='["0"' -for i in $(seq 1 $(( chunks_count - 1 )) ); do - chunks+=",\"$i\"" -done -chunks+="]" - -{ - echo "build_all=$build_all" - echo "build_libraries=$BUILD_LIBRARIES" - echo "build_static_sketches=$BUILD_STATIC_SKETCHES" - echo "chunk_count=$chunks_count" - echo "chunks=$chunks" -} >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 01ceafe9af1..668d2ab7bd8 100755 --- a/.github/scripts/sketch_utils.sh +++ b/.github/scripts/sketch_utils.sh @@ -15,13 +15,13 @@ function check_requirements { # check_requirements local requirements local requirements_or - if [ ! -f "$sdkconfig_path" ] || [ ! -f "$sketchdir/ci.json" ]; then - echo "WARNING: sdkconfig or ci.json not found. Assuming requirements are met." 1>&2 + if [ ! -f "$sdkconfig_path" ] || [ ! -f "$sketchdir/ci.yml" ]; then + echo "WARNING: sdkconfig or ci.yml not found. Assuming requirements are met." 1>&2 # Return 1 on error to force the sketch to be built and fail. This way the # CI will fail and the user will know that the sketch has a problem. else # Check if the sketch requires any configuration options (AND) - requirements=$(jq -r '.requires[]? // empty' "$sketchdir/ci.json") + requirements=$(yq eval '.requires[]' "$sketchdir/ci.yml" 2>/dev/null) if [[ "$requirements" != "null" && "$requirements" != "" ]]; then for requirement in $requirements; do requirement=$(echo "$requirement" | xargs) @@ -33,7 +33,7 @@ function check_requirements { # check_requirements fi # Check if the sketch requires any configuration options (OR) - requirements_or=$(jq -r '.requires_any[]? // empty' "$sketchdir/ci.json") + requirements_or=$(yq eval '.requires_any[]' "$sketchdir/ci.yml" 2>/dev/null) if [[ "$requirements_or" != "null" && "$requirements_or" != "" ]]; then local found=false for requirement in $requirements_or; do @@ -122,13 +122,13 @@ function build_sketch { # build_sketch [ext # precedence. Note that the following logic also falls to the default # parameters if no arguments were passed and no file was found. - if [ -z "$options" ] && [ -f "$sketchdir"/ci.json ]; then + if [ -z "$options" ] && [ -f "$sketchdir"/ci.yml ]; then # The config file could contain multiple FQBNs for one chip. If # that's the case we build one time for every FQBN. - len=$(jq -r --arg target "$target" '.fqbn[$target] | length' "$sketchdir"/ci.json) + len=$(yq eval ".fqbn.${target} | length" "$sketchdir"/ci.yml 2>/dev/null || echo 0) if [ "$len" -gt 0 ]; then - fqbn=$(jq -r --arg target "$target" '.fqbn[$target] | sort' "$sketchdir"/ci.json) + fqbn=$(yq eval ".fqbn.${target} | sort | @json" "$sketchdir"/ci.yml) fi fi @@ -138,8 +138,8 @@ function build_sketch { # build_sketch [ext len=1 - if [ -f "$sketchdir"/ci.json ]; then - fqbn_append=$(jq -r '.fqbn_append' "$sketchdir"/ci.json) + if [ -f "$sketchdir"/ci.yml ]; then + fqbn_append=$(yq eval '.fqbn_append' "$sketchdir"/ci.yml 2>/dev/null) if [ "$fqbn_append" == "null" ]; then fqbn_append="" fi @@ -156,7 +156,7 @@ function build_sketch { # build_sketch [ext esp32c6_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') esp32h2_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') esp32p4_opts=$(echo "PSRAM=enabled,USBMode=default,$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') - esp32c5_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') + esp32c5_opts=$(echo "PSRAM=enabled,$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') # Select the common part of the FQBN based on the target. The rest will be # appended depending on the passed options. @@ -229,9 +229,9 @@ function build_sketch { # build_sketch [ext sketchname=$(basename "$sketchdir") local has_requirements - if [ -f "$sketchdir"/ci.json ]; then + if [ -f "$sketchdir"/ci.yml ]; then # If the target is listed as false, skip the sketch. Otherwise, include it. - is_target=$(jq -r --arg target "$target" '.targets[$target]' "$sketchdir"/ci.json) + is_target=$(yq eval ".targets.${target}" "$sketchdir"/ci.yml 2>/dev/null) if [[ "$is_target" == "false" ]]; then echo "Skipping $sketchname for target $target" exit 0 @@ -244,6 +244,14 @@ function build_sketch { # build_sketch [ext fi fi + # Install libraries from ci.yml if they exist + install_libs -ai "$ide_path" -s "$sketchdir" + install_result=$? + if [ $install_result -ne 0 ]; then + echo "ERROR: Library installation failed for $sketchname" >&2 + exit $install_result + fi + ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" if [ -n "$ARDUINO_BUILD_DIR" ]; then build_dir="$ARDUINO_BUILD_DIR" @@ -286,6 +294,11 @@ function build_sketch { # build_sketch [ext exit "$exit_status" fi + # Copy ci.yml alongside compiled binaries for later consumption by reporting tools + if [ -f "$sketchdir/ci.yml" ]; then + cp -f "$sketchdir/ci.yml" "$build_dir/ci.yml" 2>/dev/null || true + fi + if [ -n "$log_compilation" ]; then #Extract the program storage space and dynamic memory usage in bytes and percentage in separate variables from the output, just the value without the string flash_bytes=$(grep -oE 'Sketch uses ([0-9]+) bytes' "$output_file" | awk '{print $3}') @@ -329,6 +342,10 @@ function build_sketch { # build_sketch [ext echo "ERROR: Compilation failed with error code $exit_status" exit $exit_status fi + # Copy ci.yml alongside compiled binaries for later consumption by reporting tools + if [ -f "$sketchdir/ci.yml" ]; then + cp -f "$sketchdir/ci.yml" "$build_dir/ci.yml" 2>/dev/null || true + fi # $ide_path/arduino-builder -compile -logger=human -core-api-version=10810 \ # -fqbn=\"$currfqbn\" \ # -warnings="all" \ @@ -349,7 +366,7 @@ function build_sketch { # build_sketch [ext unset options } -function count_sketches { # count_sketches [target] [file] [ignore-requirements] +function count_sketches { # count_sketches [target] [ignore-requirements] [file] local path=$1 local target=$2 local ignore_requirements=$3 @@ -368,7 +385,7 @@ function count_sketches { # count_sketches [target] [file] [ignore-requir fi if [ -f "$file" ]; then - sketches=$(cat "$file") + sketches=$(cat "$file" | sort) else sketches=$(find "$path" -name '*.ino' | sort) fi @@ -386,9 +403,9 @@ function count_sketches { # count_sketches [target] [file] [ignore-requir if [[ "$sketchdirname.ino" != "$sketchname" ]]; then continue - elif [[ -n $target ]] && [[ -f $sketchdir/ci.json ]]; then + elif [[ -n $target ]] && [[ -f $sketchdir/ci.yml ]]; then # If the target is listed as false, skip the sketch. Otherwise, include it. - is_target=$(jq -r --arg target "$target" '.targets[$target]' "$sketchdir"/ci.json) + is_target=$(yq eval ".targets.${target}" "$sketchdir"/ci.yml 2>/dev/null) if [[ "$is_target" == "false" ]]; then continue fi @@ -580,6 +597,160 @@ function build_sketches { # build_sketches &2 || printf '%s\n' "$out" >&2 + fi +} + +function install_libs { # install_libs [-v] + local ide_path="" + local sketchdir="" + local verbose=false + + while [ -n "$1" ]; do + case "$1" in + -ai ) shift; ide_path=$1 ;; + -s ) shift; sketchdir=$1 ;; + -v ) verbose=true ;; + * ) + echo "ERROR: Unknown argument: $1" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + ;; + esac + shift + done + + if [ -z "$ide_path" ]; then + echo "ERROR: IDE path not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ -z "$sketchdir" ]; then + echo "ERROR: Sketch directory not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ ! -f "$ide_path/arduino-cli" ]; then + echo "ERROR: arduino-cli not found at $ide_path/arduino-cli" >&2 + return 1 + fi + + if [ ! -f "$sketchdir/ci.yml" ]; then + [ "$verbose" = true ] && echo "No ci.yml found in $sketchdir, skipping library installation" + return 0 + fi + if ! yq eval '.' "$sketchdir/ci.yml" >/dev/null 2>&1; then + echo "ERROR: $sketchdir/ci.yml is not valid YAML" >&2 + return 1 + fi + + local libs_type + libs_type=$(yq eval '.libs | type' "$sketchdir/ci.yml" 2>/dev/null) + if [ -z "$libs_type" ] || [ "$libs_type" = "null" ] || [ "$libs_type" = "!!null" ]; then + [ "$verbose" = true ] && echo "No libs field found in ci.yml, skipping library installation" + return 0 + elif [ "$libs_type" != "!!seq" ]; then + echo "ERROR: libs field in ci.yml must be an array, found: $libs_type" >&2 + return 1 + fi + + local libs_count + libs_count=$(yq eval '.libs | length' "$sketchdir/ci.yml" 2>/dev/null) + if [ "$libs_count" -eq 0 ]; then + [ "$verbose" = true ] && echo "libs array is empty in ci.yml, skipping library installation" + return 0 + fi + + echo "Installing $libs_count libraries from $sketchdir/ci.yml" + + local needs_unsafe=false + local original_unsafe_setting="" + local libs + libs=$(yq eval '.libs[]' "$sketchdir/ci.yml" 2>/dev/null) + + # Detect any git-like URL (GitHub/GitLab/Bitbucket/self-hosted/ssh) + for lib in $libs; do + if is_git_like_url "$lib"; then + needs_unsafe=true + break + fi + done + + if [ "$needs_unsafe" = true ]; then + [ "$verbose" = true ] && echo "Checking current unsafe install setting..." + original_unsafe_setting=$("$ide_path/arduino-cli" config get library.enable_unsafe_install 2>/dev/null || echo "false") + if [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Enabling unsafe installs for Git URLs..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install true >/dev/null 2>&1 || \ + echo "WARNING: Failed to enable unsafe installs, Git URL installs may fail" >&2 + else + [ "$verbose" = true ] && echo "Unsafe installs already enabled" + fi + fi + + local rc=0 install_status=0 output="" + for lib in $libs; do + [ "$verbose" = true ] && echo "Processing library: $lib" + + if is_git_like_url "$lib"; then + [ "$verbose" = true ] && echo "Installing library from git URL: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install --git-url "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install --git-url "$lib" 2>&1) + install_status=$? + fi + else + [ "$verbose" = true ] && echo "Installing library by name: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install "$lib" 2>&1) + install_status=$? + fi + fi + + # Treat "already installed"/"up to date" as success (idempotent) + if [ $install_status -ne 0 ] && echo "$output" | grep -qiE 'already installed|up to date'; then + install_status=0 + fi + + if [ "$verbose" != true ]; then + print_err_warnings "$install_status" "$output" + fi + + if [ $install_status -ne 0 ]; then + echo "ERROR: Failed to install library: $lib" >&2 + rc=$install_status + break + else + [ "$verbose" = true ] && echo "Successfully installed library: $lib" + fi + done + + if [ "$needs_unsafe" = true ] && [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Restoring original unsafe install setting..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install false >/dev/null 2>&1 || true + fi + + [ $rc -eq 0 ] && echo "Library installation completed" + return $rc +} + + USAGE=" USAGE: ${0} [command] [options] Available commands: @@ -587,6 +758,7 @@ Available commands: build: Build a sketch. chunk_build: Build a chunk of sketches. check_requirements: Check if target meets sketch requirements. + install_libs: Install libraries from ci.yml file. " cmd=$1 @@ -606,6 +778,8 @@ case "$cmd" in ;; "check_requirements") check_requirements "$@" ;; + "install_libs") install_libs "$@" + ;; *) echo "ERROR: Unrecognized command" echo "$USAGE" diff --git a/.github/scripts/tests_matrix.sh b/.github/scripts/tests_matrix.sh index a8baf2ce275..6b4f8001fc3 100644 --- a/.github/scripts/tests_matrix.sh +++ b/.github/scripts/tests_matrix.sh @@ -1,28 +1,49 @@ #!/bin/bash -build_types="'validation'" -hw_types="'validation'" -wokwi_types="'validation'" -qemu_types="'validation'" +# QEMU is disabled for now +qemu_enabled="false" + +build_types='"validation"' +hw_types='"validation"' +wokwi_types='"validation"' +qemu_types='"validation"' if [[ $IS_PR != 'true' ]] || [[ $PERFORMANCE_ENABLED == 'true' ]]; then - build_types+=",'performance'" - hw_types+=",'performance'" - #wokwi_types+=",'performance'" - #qemu_types+=",'performance'" + build_types+=',"performance"' + hw_types+=',"performance"' + #wokwi_types+=',"performance"' + #qemu_types+=',"performance"' fi -targets="'esp32','esp32s2','esp32s3','esp32c3','esp32c6','esp32h2','esp32p4'" +hw_targets='"esp32","esp32s2","esp32s3","esp32c3","esp32c5","esp32c6","esp32h2","esp32p4"' +wokwi_targets='"esp32","esp32s2","esp32s3","esp32c3","esp32c6","esp32h2","esp32p4"' +qemu_targets='"esp32","esp32c3"' + +# The build targets should be the sum of the hw, wokwi and qemu targets without duplicates +build_targets=$(echo "$hw_targets,$wokwi_targets,$qemu_targets" | tr ',' '\n' | sort -u | tr '\n' ',' | sed 's/,$//') mkdir -p info -echo "[$wokwi_types]" > info/wokwi_types.txt -echo "[$targets]" > info/targets.txt +# Create a single JSON file with all test matrix information +cat > info/test_matrix.json <> "$GITHUB_OUTPUT" diff --git a/.github/scripts/tests_run.sh b/.github/scripts/tests_run.sh index 1c4bee79742..e56518e9745 100755 --- a/.github/scripts/tests_run.sh +++ b/.github/scripts/tests_run.sh @@ -17,8 +17,8 @@ function run_test { sketchname=$(basename "$sketchdir") test_type=$(basename "$(dirname "$sketchdir")") - if [ "$options" -eq 0 ] && [ -f "$sketchdir"/ci.json ]; then - len=$(jq -r --arg target "$target" '.fqbn[$target] | length' "$sketchdir"/ci.json) + if [ "$options" -eq 0 ] && [ -f "$sketchdir"/ci.yml ]; then + len=$(yq eval ".fqbn.${target} | length" "$sketchdir"/ci.yml 2>/dev/null || echo 0) if [ "$len" -eq 0 ]; then len=1 fi @@ -32,10 +32,10 @@ function run_test { sdkconfig_path="$HOME/.arduino/tests/$target/$sketchname/build0.tmp/sdkconfig" fi - if [ -f "$sketchdir"/ci.json ]; then + if [ -f "$sketchdir"/ci.yml ]; then # If the target or platform is listed as false, skip the sketch. Otherwise, include it. - is_target=$(jq -r --arg target "$target" '.targets[$target]' "$sketchdir"/ci.json) - selected_platform=$(jq -r --arg platform "$platform" '.platforms[$platform]' "$sketchdir"/ci.json) + is_target=$(yq eval ".targets.${target}" "$sketchdir"/ci.yml 2>/dev/null) + selected_platform=$(yq eval ".platforms.${platform}" "$sketchdir"/ci.yml 2>/dev/null) if [[ $is_target == "false" ]] || [[ $selected_platform == "false" ]]; then printf "\033[93mSkipping %s test for %s, platform: %s\033[0m\n" "$sketchname" "$target" "$platform" @@ -68,17 +68,17 @@ function run_test { fqbn="Default" if [ "$len" -ne 1 ]; then - fqbn=$(jq -r --arg target "$target" --argjson i "$i" '.fqbn[$target] | sort | .[$i]' "$sketchdir"/ci.json) - elif [ -f "$sketchdir"/ci.json ]; then - has_fqbn=$(jq -r --arg target "$target" '.fqbn[$target]' "$sketchdir"/ci.json) + fqbn=$(yq eval ".fqbn.${target} | sort | .[${i}]" "$sketchdir"/ci.yml 2>/dev/null) + elif [ -f "$sketchdir"/ci.yml ]; then + has_fqbn=$(yq eval ".fqbn.${target}" "$sketchdir"/ci.yml 2>/dev/null) if [ "$has_fqbn" != "null" ]; then - fqbn=$(jq -r --arg target "$target" '.fqbn[$target] | .[0]' "$sketchdir"/ci.json) + fqbn=$(yq eval ".fqbn.${target} | .[0]" "$sketchdir"/ci.yml 2>/dev/null) fi fi printf "\033[95mRunning test: %s -- Config: %s\033[0m\n" "$sketchname" "$fqbn" if [ "$erase_flash" -eq 1 ]; then - esptool.py -c "$target" erase_flash + esptool -c "$target" erase-flash fi if [ "$len" -ne 1 ]; then @@ -88,14 +88,10 @@ function run_test { fi if [ $platform == "wokwi" ]; then - extra_args=("--target" "$target" "--embedded-services" "arduino,wokwi" "--wokwi-timeout=$wokwi_timeout") - if [[ -f "$sketchdir/scenario.yaml" ]]; then - extra_args+=("--wokwi-scenario" "$sketchdir/scenario.yaml") - fi + extra_args=("--target" "$target" "--embedded-services" "arduino,wokwi") if [[ -f "$sketchdir/diagram.$target.json" ]]; then extra_args+=("--wokwi-diagram" "$sketchdir/diagram.$target.json") fi - elif [ $platform == "qemu" ]; then PATH=$HOME/qemu/bin:$PATH extra_args=("--embedded-services" "qemu" "--qemu-image-path" "$build_dir/$sketchname.ino.merged.bin") @@ -114,15 +110,23 @@ function run_test { rm "$sketchdir"/diagram.json 2>/dev/null || true + local wifi_args="" + if [ -n "$wifi_ssid" ]; then + wifi_args="--wifi-ssid \"$wifi_ssid\"" + fi + if [ -n "$wifi_password" ]; then + wifi_args="$wifi_args --wifi-password \"$wifi_password\"" + fi + result=0 - printf "\033[95mpytest \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" - bash -c "set +e; pytest \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q}; exit \$?" || result=$? + printf "\033[95mpytest -s \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" "$wifi_args" + bash -c "set +e; pytest -s \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q} $wifi_args; exit \$?" || result=$? printf "\n" if [ $result -ne 0 ]; then result=0 printf "\033[95mRetrying test: %s -- Config: %s\033[0m\n" "$sketchname" "$i" - printf "\033[95mpytest \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" - bash -c "set +e; pytest \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q}; exit \$?" || result=$? + printf "\033[95mpytest -s \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" "$wifi_args" + bash -c "set +e; pytest -s \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q} $wifi_args; exit \$?" || result=$? printf "\n" if [ $result -ne 0 ]; then printf "\033[91mFailed test: %s -- Config: %s\033[0m\n\n" "$sketchname" "$i" @@ -137,10 +141,11 @@ SCRIPTS_DIR="./.github/scripts" COUNT_SKETCHES="${SCRIPTS_DIR}/sketch_utils.sh count" platform="hardware" -wokwi_timeout=60000 chunk_run=0 options=0 erase=0 +wifi_ssid="" +wifi_password="" while [ -n "$1" ]; do case $1 in @@ -155,8 +160,6 @@ while [ -n "$1" ]; do platform="qemu" ;; -W ) - shift - wokwi_timeout=$1 if [[ -z $WOKWI_CLI_TOKEN ]]; then echo "Wokwi CLI token is not set" exit 1 @@ -193,6 +196,14 @@ while [ -n "$1" ]; do shift test_type=$1 ;; + -wifi-ssid ) + shift + wifi_ssid=$1 + ;; + -wifi-password ) + shift + wifi_password=$1 + ;; * ) break ;; diff --git a/.github/scripts/update-version.sh b/.github/scripts/update-version.sh index 59a95d01105..814cf24afd6 100755 --- a/.github/scripts/update-version.sh +++ b/.github/scripts/update-version.sh @@ -2,53 +2,74 @@ # Disable shellcheck warning about using 'cat' to read a file. # shellcheck disable=SC2002 +# Exit on any error and make pipelines fail if any command fails +set -e +set -o pipefail + # For reference: add tools for all boards by replacing one line in each board # "[board].upload.tool=esptool_py" to "[board].upload.tool=esptool_py\n[board].upload.tool.default=esptool_py\n[board].upload.tool.network=esp_ota" #cat boards.txt | sed "s/\([a-zA-Z0-9_\-]*\)\.upload\.tool\=esptool_py/\1\.upload\.tool\=esptool_py\\n\1\.upload\.tool\.default\=esptool_py\\n\1\.upload\.tool\.network\=esp_ota/" -if [ ! $# -eq 3 ]; then +if [ ! $# -eq 1 ]; then echo "Bad number of arguments: $#" >&2 - echo "usage: $0 " >&2 + echo "usage: $0 " >&2 exit 1 fi -re='^[0-9]+$' -if [[ ! $1 =~ $re ]] || [[ ! $2 =~ $re ]] || [[ ! $3 =~ $re ]] ; then - echo "error: Not a valid version: $1.$2.$3" >&2 - echo "usage: $0 " >&2 +# Version must be in the format of X.Y.Z or X.Y.Z-abc123 (POSIX ERE) +re='^[0-9]+\.[0-9]+\.[0-9]+(-[A-Za-z]+[0-9]*)?$' +version=$1 + +if [[ ! $version =~ $re ]] ; then + echo "error: Not a valid version: $version" >&2 + echo "usage: $0 " >&2 exit 1 fi -ESP_ARDUINO_VERSION_MAJOR="$1" -ESP_ARDUINO_VERSION_MINOR="$2" -ESP_ARDUINO_VERSION_PATCH="$3" -ESP_ARDUINO_VERSION="$ESP_ARDUINO_VERSION_MAJOR.$ESP_ARDUINO_VERSION_MINOR.$ESP_ARDUINO_VERSION_PATCH" +ESP_ARDUINO_VERSION_MAJOR=$(echo "$version" | cut -d. -f1) +ESP_ARDUINO_VERSION_MINOR=$(echo "$version" | cut -d. -f2) +ESP_ARDUINO_VERSION_PATCH=$(echo "$version" | cut -d. -f3 | sed 's/[^0-9].*//') # Remove non-numeric suffixes like RC1, alpha, beta +ESP_ARDUINO_VERSION_CLEAN="$ESP_ARDUINO_VERSION_MAJOR.$ESP_ARDUINO_VERSION_MINOR.$ESP_ARDUINO_VERSION_PATCH" -# Get ESP-IDF version from push.yml (this way we can ensure that the version is correct even if the local libs are not up to date) -ESP_IDF_VERSION=$(grep "idf_ver:" .github/workflows/push.yml | sed 's/.*release-v\([^"]*\).*/\1/') +# Get ESP-IDF version from build_component.yml (this way we can ensure that the version is correct even if the local libs are not up to date) +ESP_IDF_VERSION=$(grep -m 1 "default:" .github/workflows/build_component.yml | sed 's/.*release-v\([^"]*\).*/\1/') if [ -z "$ESP_IDF_VERSION" ]; then - echo "Error: ESP-IDF version not found in push.yml" >&2 + echo "Error: ESP-IDF version not found in build_component.yml" >&2 exit 1 fi -echo "New Arduino Version: $ESP_ARDUINO_VERSION" +echo "New Arduino Version: $version" echo "ESP-IDF Version: $ESP_IDF_VERSION" +echo "Updating issue template..." +if ! grep -q "v$version" .github/ISSUE_TEMPLATE/Issue-report.yml; then + cat .github/ISSUE_TEMPLATE/Issue-report.yml | \ + sed "s/.*\- latest master .*/ - latest master \(checkout manually\)\\n - v$version/g" > __issue-report.yml && mv __issue-report.yml .github/ISSUE_TEMPLATE/Issue-report.yml + echo "Issue template updated with version v$version" +else + echo "Version v$version already exists in issue template, skipping update" +fi + +echo "Updating GitLab variables..." +cat .gitlab/workflows/common.yml | \ +sed "s/ESP_IDF_VERSION:.*/ESP_IDF_VERSION: \"$ESP_IDF_VERSION\"/g" | \ +sed "s/ESP_ARDUINO_VERSION:.*/ESP_ARDUINO_VERSION: \"$ESP_ARDUINO_VERSION_CLEAN\"/g" > .gitlab/workflows/__common.yml && mv .gitlab/workflows/__common.yml .gitlab/workflows/common.yml + echo "Updating platform.txt..." -cat platform.txt | sed "s/version=.*/version=$ESP_ARDUINO_VERSION/g" > __platform.txt && mv __platform.txt platform.txt +cat platform.txt | sed "s/version=.*/version=$ESP_ARDUINO_VERSION_CLEAN/g" > __platform.txt && mv __platform.txt platform.txt echo "Updating package.json..." -cat package.json | sed "s/.*\"version\":.*/ \"version\": \"$ESP_ARDUINO_VERSION\",/g" > __package.json && mv __package.json package.json +cat package.json | sed "s/.*\"version\":.*/ \"version\": \"$ESP_ARDUINO_VERSION_CLEAN\",/g" > __package.json && mv __package.json package.json echo "Updating docs/conf_common.py..." cat docs/conf_common.py | \ -sed "s/.. |version| replace:: .*/.. |version| replace:: $ESP_ARDUINO_VERSION/g" | \ +sed "s/.. |version| replace:: .*/.. |version| replace:: $ESP_ARDUINO_VERSION_CLEAN/g" | \ sed "s/.. |idf_version| replace:: .*/.. |idf_version| replace:: $ESP_IDF_VERSION/g" > docs/__conf_common.py && mv docs/__conf_common.py docs/conf_common.py echo "Updating .gitlab/workflows/common.yml..." cat .gitlab/workflows/common.yml | \ sed "s/ESP_IDF_VERSION:.*/ESP_IDF_VERSION: \"$ESP_IDF_VERSION\"/g" | \ -sed "s/ESP_ARDUINO_VERSION:.*/ESP_ARDUINO_VERSION: \"$ESP_ARDUINO_VERSION\"/g" > .gitlab/workflows/__common.yml && mv .gitlab/workflows/__common.yml .gitlab/workflows/common.yml +sed "s/ESP_ARDUINO_VERSION:.*/ESP_ARDUINO_VERSION: \"$ESP_ARDUINO_VERSION_CLEAN\"/g" > .gitlab/workflows/__common.yml && mv .gitlab/workflows/__common.yml .gitlab/workflows/common.yml echo "Updating cores/esp32/esp_arduino_version.h..." cat cores/esp32/esp_arduino_version.h | \ @@ -60,7 +81,7 @@ libraries=$(find libraries -maxdepth 1 -mindepth 1 -type d -exec basename {} \;) for lib in $libraries; do if [ -f "libraries/$lib/library.properties" ]; then echo "Updating Library $lib..." - cat "libraries/$lib/library.properties" | sed "s/version=.*/version=$ESP_ARDUINO_VERSION/g" > "libraries/$lib/__library.properties" && mv "libraries/$lib/__library.properties" "libraries/$lib/library.properties" + cat "libraries/$lib/library.properties" | sed "s/version=.*/version=$ESP_ARDUINO_VERSION_CLEAN/g" > "libraries/$lib/__library.properties" && mv "libraries/$lib/__library.properties" "libraries/$lib/library.properties" fi done diff --git a/.github/scripts/update_esptool.py b/.github/scripts/update_esptool.py old mode 100644 new mode 100755 index d99462fcb8f..0757e8085ea --- a/.github/scripts/update_esptool.py +++ b/.github/scripts/update_esptool.py @@ -33,6 +33,7 @@ import os import shutil import stat +import subprocess import tarfile import zipfile import hashlib @@ -201,10 +202,34 @@ def get_release_info(version): response.raise_for_status() return response.json() +def create_branch_and_commit(version, json_path): + """Create a new branch and commit the changes to it.""" + branch_name = f"update-esptool-{version}" + commit_message = f"change(esptool): Upgrade to version {version}" + + try: + # Create and checkout new branch + subprocess.run(["git", "checkout", "-b", branch_name], check=True, capture_output=True, text=True) + print(f"Created and switched to new branch: {branch_name}") + + # Stage the JSON file + subprocess.run(["git", "add", str(json_path)], check=True, capture_output=True, text=True) + print(f"Staged file: {json_path}") + + # Commit the changes + subprocess.run(["git", "commit", "-m", commit_message], check=True, capture_output=True, text=True) + print(f"Committed changes with message: {commit_message}") + + except subprocess.CalledProcessError as e: + print(f"Git operation failed: {e}") + print(f"Command output: {e.stderr if e.stderr else e.stdout}") + raise + def main(): parser = argparse.ArgumentParser(description="Repack esptool and update JSON metadata.") parser.add_argument("version", help="Version of the esptool (e.g. 5.0.dev1)") parser.add_argument("-l", "--local", dest="base_folder", help="Enable local build mode and set the base folder with unpacked artifacts") + parser.add_argument("-c", "--commit", action="store_true", help="Automatically create a new branch and commit the JSON file changes") args = parser.parse_args() script_dir = Path(__file__).resolve().parent @@ -232,5 +257,10 @@ def main(): shutil.move(tmp_json_path, json_path) print(f"Done. JSON updated at {json_path}") + # Auto-commit if requested + if args.commit: + print("Auto-commit enabled. Creating branch and committing changes...") + create_branch_and_commit(args.version, json_path) + if __name__ == "__main__": main() diff --git a/.github/scripts/validate_board.sh b/.github/scripts/validate_board.sh new file mode 100755 index 00000000000..5c142ca8c19 --- /dev/null +++ b/.github/scripts/validate_board.sh @@ -0,0 +1,596 @@ +#!/bin/bash + +# Board validation script for ESP32 Arduino Core +# This script validates board definitions in boards.txt + +set -e + +# Required properties for all boards +REQUIRED_PROPERTIES=("upload.flags" "upload.extra_flags") + +# Function to print output +print_error() { + echo "Error: $1" +} + +print_success() { + echo "✓ $1" +} + +# Function to validate a single board +validate_board() { + local board_name="$1" + local boards_file="boards.txt" + + echo "Validating board: $board_name" + echo "" + + # Rule 1: Check build.board format + echo "Rule 1: Build Board Format Validation" + echo "=====================================" + validate_build_board_format "$board_name" "$boards_file" + echo "" + + # Rule 2: Check for required board properties + echo "Rule 2: Required Properties Validation" + echo "======================================" + validate_required_properties "$board_name" "$boards_file" + echo "" + + # Rule 3: Check for valid partition schemes for available flash sizes + echo "Rule 3: Partition Scheme Validation" + echo "===================================" + validate_partition_schemes "$board_name" "$boards_file" + echo "" + + # Rule 4: Check for VID and PID consistency + echo "Rule 4: VID/PID Consistency Validation" + echo "=====================================" + validate_vid_pid_consistency "$board_name" "$boards_file" + echo "" + + # Rule 5: Check for DebugLevel menu + echo "Rule 5: DebugLevel Menu Validation" + echo "==================================" + validate_debug_level_menu "$board_name" "$boards_file" + echo "" + + # Rule 6: Check for duplicate lines + echo "Rule 6: Duplicate Lines Validation" + echo "==================================" + validate_no_duplicates "$board_name" "$boards_file" + echo "" + + # Add more validation rules here as needed + echo "==========================================" + print_success "ALL VALIDATION RULES PASSED for board '$board_name'" + echo "==========================================" +} + +# Rule 1: Check build.board format +validate_build_board_format() { + local board_name="$1" + local boards_file="$2" + + # Get the build.board value + local build_board_value + build_board_value=$(grep "^$board_name.build.board=" "$boards_file" | cut -d'=' -f2) + + if [ -z "$build_board_value" ]; then + print_error "build.board property not found for '$board_name'" + exit 1 + fi + + # Check for invalid characters (anything that's not uppercase letter, number, or underscore) + if echo "$build_board_value" | grep -q '[^A-Z0-9_]'; then + local invalid_chars + invalid_chars=$(echo "$build_board_value" | grep -o '[^A-Z0-9_]' | sort -u | tr -d '\n') + print_error "$board_name.build.board contains invalid characters: '$invalid_chars' (only A-Z, 0-9, and _ are allowed)" + exit 1 + fi + + # Check if it's all uppercase + if echo "$build_board_value" | grep -q '[a-z]'; then + print_error "build.board must be uppercase: '$build_board_value' (found lowercase letters)" + exit 1 + fi + + echo " ✓ build.board is valid: '$build_board_value'" +} + +# Rule 2: Check for required board properties +validate_required_properties() { + local board_name="$1" + local boards_file="$2" + + local missing_props=() + + for prop in "${REQUIRED_PROPERTIES[@]}"; do + if ! grep -q "^$board_name.$prop=" "$boards_file"; then + missing_props+=("$prop") + fi + done + + if [ ${#missing_props[@]} -gt 0 ]; then + print_error "Missing required properties for board '$board_name':" + printf ' - %s\n' "${missing_props[@]}" + exit 1 + fi + + echo " ✓ Required properties validation completed" +} + + +# Rule 3: Check for valid partition schemes for available flash sizes +validate_partition_schemes() { + local board_name="$1" + local boards_file="$2" + + # Get all available flash sizes for this board + local flash_sizes + flash_sizes=$(grep "^$board_name.menu.FlashSize\." "$boards_file" | grep "\.build\.flash_size=" | cut -d'=' -f2 | sort -V) + + # Check if board has menu.FlashSize entries + if [ -z "$flash_sizes" ]; then + # If no menu.FlashSize entries, check if board has build.flash_size entry at least + local has_flash_size + has_flash_size=$(grep "^$board_name\." "$boards_file" | grep "\.build\.flash_size=" | head -1) + + if [ -z "$has_flash_size" ]; then + print_error "No flash size options found for board '$board_name' (needs build.flash_size entry at least)" + exit 1 + else + # Extract flash size from build.flash_size entry + local flash_size_value + flash_size_value=$(echo "$has_flash_size" | cut -d'=' -f2) + flash_sizes="$flash_size_value" + fi + fi + + # Convert flash sizes to MB for comparison + local flash_sizes_mb=() + while IFS= read -r size; do + if [[ "$size" =~ ^([0-9]+)MB$ ]]; then + flash_sizes_mb+=("${BASH_REMATCH[1]}") + fi + done <<< "$flash_sizes" + + # Find the maximum flash size available + local max_flash_mb=0 + for size_mb in "${flash_sizes_mb[@]}"; do + if [ "$size_mb" -gt "$max_flash_mb" ]; then + max_flash_mb="$size_mb" + fi + done + + echo " ✓ Flash configuration found - maximum size: ${max_flash_mb}MB" + + # Find all partition schemes for this board + local partition_schemes + partition_schemes=$(grep "^$board_name.menu.PartitionScheme\." "$boards_file" | grep -v "\.build\." | grep -v "\.upload\." | sed 's/.*\.PartitionScheme\.\([^=]*\)=.*/\1/' | sort -u) + + if [ -n "$partition_schemes" ]; then + # Validate each partition scheme against the maximum flash size + while IFS= read -r scheme; do + validate_partition_scheme_size "$scheme" "$max_flash_mb" "$board_name" "$boards_file" + done <<< "$partition_schemes" + fi + + + echo " ✓ Partition scheme validation completed" +} + +# Helper function to validate individual partition scheme +validate_partition_scheme_size() { + local scheme="$1" + local max_flash_mb="$2" + local board_name="$3" + local boards_file="$4" + + # Extract size from partition scheme name (e.g., "default_8MB" -> 8) + if [[ "$scheme" =~ _([0-9]+)MB$ ]]; then + local scheme_size_mb="${BASH_REMATCH[1]}" + + if [ "$scheme_size_mb" -gt "$max_flash_mb" ]; then + print_error "Partition scheme '$scheme' (${scheme_size_mb}MB) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + else + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (size indicator: ${scheme_size_mb}MB)" + fi + elif [[ "$scheme" =~ _([0-9]+)M$ ]]; then + # Handle cases like "default_8M" (without B) + local scheme_size_mb="${BASH_REMATCH[1]}" + + if [ "$scheme_size_mb" -gt "$max_flash_mb" ]; then + print_error "Partition scheme '$scheme' (${scheme_size_mb}MB) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + else + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (size indicator: ${scheme_size_mb}MB)" + fi + elif [[ "$scheme" =~ _([0-9]+)$ ]]; then + # Handle cases like "esp_sr_16" (just number at end) + local scheme_size_mb="${BASH_REMATCH[1]}" + + if [ "$scheme_size_mb" -gt "$max_flash_mb" ]; then + print_error "Partition scheme '$scheme' (${scheme_size_mb}MB) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + else + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (size indicator: ${scheme_size_mb}MB)" + fi + else + # For schemes without size in name, check description for size indicators + local description_text + description_text=$(grep "^$board_name.menu.PartitionScheme\.$scheme=" "$boards_file" | cut -d'=' -f2) + + # First check main description for size indicators (before brackets) + # Look for the largest size indicator in the main description + local main_description_size_mb=0 + local main_description_size_text="" + + # Check for MB and M values in main description (before brackets) + local main_part=$(echo "$description_text" | sed 's/(.*//') # Remove bracket content + + # Extract M values (not followed by B) and MB values + local m_values=$(echo "$main_part" | grep -oE '([0-9]+\.?[0-9]*)M' | grep -v 'MB' | sed 's/M$//') + local mb_values=$(echo "$main_part" | grep -oE '([0-9]+\.?[0-9]*)MB' | sed 's/MB//') + + # Combine both M and MB values + local all_mb_values=$(echo -e "$m_values\n$mb_values" | grep -v '^$') + + # Find the largest MB value in main description + local largest_mb_int=0 + while IFS= read -r value; do + if [[ "$value" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + local whole="${BASH_REMATCH[1]}" + local decimal="${BASH_REMATCH[2]}" + local value_int=$((whole * 10 + decimal)) + elif [[ "$value" =~ ^([0-9]+)$ ]]; then + local value_int=$((value * 10)) + else + continue + fi + + if [ "$value_int" -gt "$largest_mb_int" ]; then + largest_mb_int=$value_int + main_description_size_text="${value}M" + fi + done <<< "$all_mb_values" + + if [ "$largest_mb_int" -gt 0 ]; then + # Found size in main description + if [ "$largest_mb_int" -gt $((max_flash_mb * 10)) ]; then + print_error "Partition scheme '$scheme' (${main_description_size_text} from description) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + else + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (size from description: ${main_description_size_text})" + fi + else + # No size in main description, check bracket content + local bracket_content + bracket_content=$(echo "$description_text" | grep -oE '\([^)]+\)' | head -1) + + if [ -n "$bracket_content" ]; then + # Calculate total size from all components in brackets + local total_size_mb=0 + + # Extract and sum MB values + local mb_sum=0 + while IFS= read -r value; do + if [[ "$value" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + local whole="${BASH_REMATCH[1]}" + local decimal="${BASH_REMATCH[2]}" + # Convert decimal to integer (e.g., 1.3 -> 13, 6.93 -> 69) + # Handle multi-digit decimals: 6.93 -> 6*10 + 9 = 69 (round down) + local decimal_int=$((decimal / 10)) + mb_sum=$((mb_sum + whole * 10 + decimal_int)) + elif [[ "$value" =~ ^([0-9]+)$ ]]; then + mb_sum=$((mb_sum + value * 10)) + fi + done < <(echo "$bracket_content" | grep -oE '([0-9]+\.?[0-9]*)MB' | sed 's/MB//') + + # Extract and sum KB values (convert to MB tenths) + local kb_sum=0 + while IFS= read -r value; do + if [[ "$value" =~ ^([0-9]+)\.([0-9]+)$ ]]; then + local whole="${BASH_REMATCH[1]}" + local decimal="${BASH_REMATCH[2]}" + # Convert KB to MB tenths: (whole.decimal * 10) / 1024, rounded + local kb_tenths=$((whole * 10 + decimal)) + kb_sum=$((kb_sum + (kb_tenths * 10 + 512) / 1024)) + elif [[ "$value" =~ ^([0-9]+)$ ]]; then + # Convert KB to MB tenths: value * 10 / 1024, rounded + kb_sum=$((kb_sum + (value * 10 + 512) / 1024)) + fi + done < <(echo "$bracket_content" | grep -oE '([0-9]+\.?[0-9]*)KB' | sed 's/KB//') + + # Sum all values and convert back to MB (divide by 10, rounded) + total_size_mb=$(( (mb_sum + kb_sum + 5) / 10 )) + + if [ "$total_size_mb" -gt 0 ]; then + # Found size in description + if [ "$total_size_mb" -gt "$max_flash_mb" ]; then + print_error "Partition scheme '$scheme' (${total_size_mb}MB from description) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + else + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (size from description: ${total_size_mb}MB)" + fi + else + # No size indicator found in brackets, check upload maximum size + validate_scheme_upload_size "$scheme" "$board_name" "$boards_file" "$max_flash_mb" + fi + else + # No brackets found, check upload maximum size + validate_scheme_upload_size "$scheme" "$board_name" "$boards_file" "$max_flash_mb" + fi + fi + fi +} + +# Helper function to validate upload maximum size for a specific partition scheme +validate_scheme_upload_size() { + local scheme="$1" + local board_name="$2" + local boards_file="$3" + local max_flash_mb="$4" + + # Get upload maximum size for this specific scheme + local upload_size + upload_size=$(grep "^$board_name.menu.PartitionScheme\.$scheme\." "$boards_file" | grep "\.upload\.maximum_size=" | head -1 | cut -d'=' -f2) + + if [ -z "$upload_size" ]; then + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (no upload size limit)" + return 0 + fi + + # Convert flash size to bytes for comparison + local max_flash_bytes=$((max_flash_mb * 1024 * 1024)) + + # Check upload size against maximum flash size + if [ "$upload_size" -gt "$max_flash_bytes" ]; then + local upload_mb=$(( (upload_size + 524288) / 1048576 )) + print_error "Partition scheme '$scheme' upload size (${upload_mb}MB, ${upload_size} bytes) exceeds available flash size (${max_flash_mb}MB) for board '$board_name'" + exit 1 + fi + + local upload_mb=$(( (upload_size + 524288) / 1048576 )) + echo " ✓ Partition scheme '$scheme' is valid for ${max_flash_mb}MB flash (upload size: ${upload_mb}MB)" +} + +# Rule 4: Check for VID and PID consistency +validate_vid_pid_consistency() { + local board_name="$1" + local boards_file="$2" + + # Get all VID and PID entries for this board (including upload_port entries) + local vid_entries + local pid_entries + + vid_entries=$(grep "^$board_name\.vid\." "$boards_file" | sort) + pid_entries=$(grep "^$board_name\.pid\." "$boards_file" | sort) + + # Also get upload_port VID and PID entries + local upload_port_vid_entries + local upload_port_pid_entries + + upload_port_vid_entries=$(grep "^$board_name\.upload_port\..*\.vid=" "$boards_file" | sort) + upload_port_pid_entries=$(grep "^$board_name\.upload_port\..*\.pid=" "$boards_file" | sort) + + # Check for duplicate VID entries with same index but different values + local all_vid_entries="$vid_entries" + if [ -n "$upload_port_vid_entries" ]; then + all_vid_entries="$all_vid_entries +$upload_port_vid_entries" + fi + + local vid_duplicates + vid_duplicates=$(echo "$all_vid_entries" | cut -d'=' -f1 | sort | uniq -d) + + if [ -n "$vid_duplicates" ]; then + print_error "Found duplicate VID entries with different values for board '$board_name':" + echo "$vid_duplicates" + exit 1 + fi + + # Check for duplicate PID entries with same index but different values + local all_pid_entries="$pid_entries" + if [ -n "$upload_port_pid_entries" ]; then + all_pid_entries="$all_pid_entries +$upload_port_pid_entries" + fi + + local pid_duplicates + pid_duplicates=$(echo "$all_pid_entries" | cut -d'=' -f1 | sort | uniq -d) + + if [ -n "$pid_duplicates" ]; then + print_error "Found duplicate PID entries with different values for board '$board_name':" + echo "$pid_duplicates" + exit 1 + fi + + # Check for missing corresponding PID for each VID (and vice versa) + local vid_indices + local pid_indices + + # Get indices from regular vid/pid entries + local regular_vid_indices=$(echo "$vid_entries" | cut -d'=' -f1 | sed "s/^$board_name\.vid\.//" | sort -n) + local regular_pid_indices=$(echo "$pid_entries" | cut -d'=' -f1 | sed "s/^$board_name\.pid\.//" | sort -n) + + # Get indices from upload_port entries + local upload_vid_indices=$(echo "$upload_port_vid_entries" | cut -d'=' -f1 | sed "s/^$board_name\.upload_port\.//" | sed "s/\.vid$//" | sort -n) + local upload_pid_indices=$(echo "$upload_port_pid_entries" | cut -d'=' -f1 | sed "s/^$board_name\.upload_port\.//" | sed "s/\.pid$//" | sort -n) + + # Combine indices + vid_indices="$regular_vid_indices" + if [ -n "$upload_vid_indices" ]; then + vid_indices="$vid_indices +$upload_vid_indices" + fi + + pid_indices="$regular_pid_indices" + if [ -n "$upload_pid_indices" ]; then + pid_indices="$pid_indices +$upload_pid_indices" + fi + + # Check if VID and PID indices match + if [ "$vid_indices" != "$pid_indices" ]; then + print_error "VID and PID indices don't match for board '$board_name'" + echo "VID indices: $vid_indices" + echo "PID indices: $pid_indices" + exit 1 + fi + + # Check that no VID/PID combination matches esp32_family (0x303a/0x1001) + local esp32_family_vid="0x303a" + local esp32_family_pid="0x1001" + + # Check regular vid/pid entries + if [ -n "$vid_entries" ] && [ -n "$pid_entries" ]; then + while IFS= read -r vid_line; do + if [ -n "$vid_line" ]; then + local vid_index=$(echo "$vid_line" | cut -d'=' -f1 | sed "s/^$board_name\.vid\.//") + local vid_value=$(echo "$vid_line" | cut -d'=' -f2) + + # Find corresponding PID + local pid_value + pid_value=$(grep "^$board_name\.pid\.$vid_index=" "$boards_file" | cut -d'=' -f2) + + if [ "$vid_value" = "$esp32_family_vid" ] && [ "$pid_value" = "$esp32_family_pid" ]; then + print_error "Board '$board_name' VID/PID combination ($vid_value/$pid_value) matches esp32_family VID/PID (0x303a/0x1001) - this is not allowed" + exit 1 + fi + fi + done <<< "$vid_entries" + fi + + # Check upload_port vid/pid entries + if [ -n "$upload_port_vid_entries" ] && [ -n "$upload_port_pid_entries" ]; then + while IFS= read -r vid_line; do + if [ -n "$vid_line" ]; then + local vid_index=$(echo "$vid_line" | cut -d'=' -f1 | sed "s/^$board_name\.upload_port\.//" | sed "s/\.vid$//") + local vid_value=$(echo "$vid_line" | cut -d'=' -f2) + + # Find corresponding PID + local pid_value + pid_value=$(grep "^$board_name\.upload_port\.$vid_index\.pid=" "$boards_file" | cut -d'=' -f2) + + if [ "$vid_value" = "$esp32_family_vid" ] && [ "$pid_value" = "$esp32_family_pid" ]; then + print_error "Board '$board_name' upload_port VID/PID combination ($vid_value/$pid_value) matches esp32_family VID/PID (0x303a/0x1001) - this is not allowed" + exit 1 + fi + fi + done <<< "$upload_port_vid_entries" + fi + + echo " ✓ VID and PID consistency check passed" +} + +# Rule 5: Check for DebugLevel menu +validate_debug_level_menu() { + local board_name="$1" + local boards_file="$2" + + # Required DebugLevel menu options + local required_debug_levels=("none" "error" "warn" "info" "debug" "verbose") + local missing_levels=() + + # Check if DebugLevel menu exists + if ! grep -q "^$board_name.menu.DebugLevel\." "$boards_file"; then + print_error "Missing DebugLevel menu for board '$board_name'" + exit 1 + fi + + # Check each required debug level + for level in "${required_debug_levels[@]}"; do + if ! grep -q "^$board_name.menu.DebugLevel.$level=" "$boards_file"; then + missing_levels+=("$level") + fi + done + + if [ ${#missing_levels[@]} -gt 0 ]; then + print_error "Missing DebugLevel menu options for board '$board_name':" + printf ' - %s\n' "${missing_levels[@]}" + exit 1 + fi + + # Check that each debug level has the correct build.code_debug value + local code_debug_values=("0" "1" "2" "3" "4" "5") + local debug_level_index=0 + + for level in "${required_debug_levels[@]}"; do + local expected_value="${code_debug_values[$debug_level_index]}" + local actual_value + actual_value=$(grep "^$board_name.menu.DebugLevel.$level.build.code_debug=" "$boards_file" | cut -d'=' -f2) + + if [ "$actual_value" != "$expected_value" ]; then + print_error "Invalid code_debug value for DebugLevel '$level' in board '$board_name': expected '$expected_value', found '$actual_value'" + exit 1 + fi + + debug_level_index=$((debug_level_index + 1)) + done + + echo " ✓ DebugLevel menu validation completed" +} + +# Rule 6: Check for duplicate lines +validate_no_duplicates() { + local board_name="$1" + local boards_file="$2" + + # Get all lines for this board + local board_lines + board_lines=$(grep "^$board_name\." "$boards_file") + + # Extract just the property names (before =) + local property_names + property_names=$(echo "$board_lines" | cut -d'=' -f1) + + # Find duplicates + local duplicate_lines + duplicate_lines=$(echo "$property_names" | sort | uniq -d) + + if [ -n "$duplicate_lines" ]; then + print_error "Found duplicate lines for board '$board_name':" + echo "Duplicate line keys:" + echo "$duplicate_lines" + + echo "Duplicate content details:" + while IFS= read -r line_key; do + if [ -n "$line_key" ]; then + echo " Key: $line_key" + echo " Content with line numbers:" + local key_only=$(echo "$line_key" | cut -d'=' -f1) + grep -n "^$key_only=" "$boards_file" | while IFS=':' read -r line_num full_line; do + echo " Line $line_num: $full_line" + done + echo "" + fi + done <<< "$duplicate_lines" + exit 1 + fi + + echo " ✓ No duplicate lines found" +} + +# Main execution +main() { + if [ $# -ne 1 ]; then + echo "Usage: $0 " + echo "Example: $0 esp32s3" + exit 1 + fi + + local board_name="$1" + local boards_file="boards.txt" + + if [ ! -f "$boards_file" ]; then + print_error "Boards file '$boards_file' not found" + exit 1 + fi + + validate_board "$board_name" +} + +# Run main function with all arguments +main "$@" diff --git a/.github/workflows/allboards.yml b/.github/workflows/allboards.yml index 6910ad05d3f..5d834f6abbe 100644 --- a/.github/workflows/allboards.yml +++ b/.github/workflows/allboards.yml @@ -5,6 +5,9 @@ on: repository_dispatch: types: [test-boards] +permissions: + contents: read + jobs: find-boards: runs-on: ubuntu-latest @@ -36,10 +39,6 @@ jobs: with: ref: ${{ github.event.client_payload.branch }} - - run: npm install - - name: Setup jq - uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 - - id: set-test-chunks name: Set Chunks run: echo "test-chunks<> $GITHUB_OUTPUT @@ -73,6 +72,36 @@ jobs: env: FQBN: ${{ toJSON(matrix.chunk) }} + - name: Make validation script executable + run: chmod +x ./.github/scripts/validate_board.sh + + - name: Validate boards in chunk + run: | + echo "Starting board validation for chunk..." + failed_boards=() + + # Extract board names from FQBNs (remove espressif:esp32: prefix) + boards_json=$(cat fqbns.json) + board_names=$(echo "$boards_json" | jq -r '.[]' | sed 's/espressif:esp32://') + + for board in $board_names; do + echo "Validating board: $board" + if ! ./.github/scripts/validate_board.sh "$board"; then + echo "❌ Validation failed for board: $board" + failed_boards+=("$board") + else + echo "✅ Validation passed for board: $board" + fi + done + + if [ ${#failed_boards[@]} -gt 0 ]; then + echo "❌ Board validation failed for the following boards:" + printf ' - %s\n' "${failed_boards[@]}" + exit 1 + else + echo "✅ All board validations in chunk passed!" + fi + - name: Compile sketch uses: P-R-O-C-H-Y/compile-sketches@a62f069b92dc8f5053da4ac439ea6d1950cf6379 # main with: diff --git a/.github/workflows/boards.yml b/.github/workflows/boards.yml index 287e97219c4..d40ba4824ea 100644 --- a/.github/workflows/boards.yml +++ b/.github/workflows/boards.yml @@ -8,6 +8,9 @@ on: - "libraries/ESP32/examples/CI/CIBoardsTest/CIBoardsTest.ino" - ".github/workflows/boards.yml" +permissions: + contents: read + env: # It's convenient to set variables for values used multiple times in the workflow GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} @@ -24,9 +27,6 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Setup jq - uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 - - name: Get board name run: bash .github/scripts/find_new_boards.sh ${{ github.repository }} ${{github.base_ref}} @@ -49,15 +49,10 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Check if build.board is uppercase + - name: Validate board definition run: | board_name=$(echo ${{ matrix.fqbn }} | awk -F':' '{print $NF}') - if grep -q "^$board_name.build.board=[A-Z0-9_]*$" boards.txt; then - echo "$board_name.build.board is valid."; - else - echo "Error: $board_name.build.board is not uppercase!"; - exit 1; - fi + ./.github/scripts/validate_board.sh "$board_name" - name: Get libs cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 diff --git a/.github/workflows/build_component.yml b/.github/workflows/build_component.yml index 5553b4b2024..f69532d021d 100644 --- a/.github/workflows/build_component.yml +++ b/.github/workflows/build_component.yml @@ -4,15 +4,15 @@ on: workflow_dispatch: inputs: idf_ver: - description: "IDF Versions" - default: "release-v5.3,release-v5.4,release-v5.5" + description: "Comma separated list of IDF branches to build" + default: "release-v5.5" type: "string" required: true idf_targets: - description: "IDF Targets" - default: "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + description: "Comma separated list of IDF targets to build" + default: "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c5,esp32c6,esp32c61,esp32h2,esp32p4" type: "string" - required: true + required: false push: branches: - master @@ -23,20 +23,20 @@ on: - "libraries/**/*.cpp" - "libraries/**/*.c" - "libraries/**/*.h" - - "libraries/**/*.ino" - - "libraries/**/ci.json" - "idf_component_examples/**" - "idf_component.yml" - "Kconfig.projbuild" - - "CMakeLists.txt" - ".github/workflows/build_component.yml" - ".github/scripts/check-cmakelists.sh" - ".github/scripts/on-push-idf.sh" - ".github/scripts/sketch_utils.sh" + - ".github/scripts/get_affected.py" - "variants/esp32/**" - "variants/esp32c2/**" - "variants/esp32c3/**" + - "variants/esp32c5/**" - "variants/esp32c6/**" + - "variants/esp32c61/**" - "variants/esp32h2/**" - "variants/esp32p4/**" - "variants/esp32s2/**" @@ -44,6 +44,10 @@ on: - "!*.md" - "!*.txt" - "!*.properties" + - "CMakeLists.txt" + +permissions: + contents: read concurrency: group: build-component-${{github.event.pull_request.number || github.ref}} @@ -63,49 +67,133 @@ jobs: runs-on: ubuntu-latest if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} outputs: - idf_ver: ${{ steps.set-matrix.outputs.idf_ver }} - idf_target: ${{ steps.set-matrix.outputs.idf_target }} + matrix: ${{ steps.set-matrix.outputs.matrix }} + should_build: ${{ steps.affected-examples.outputs.should_build }} steps: - - name: Get IDF Version and Targets + - name: Install universal-ctags + uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3 + with: + packages: libicu74 libjansson4 libxml2 libyaml-0-2 universal-ctags + version: 1 + execute_install_scripts: true + + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + + - name: Get affected examples + id: affected-examples + env: + IS_PR: ${{ github.event_name == 'pull_request' }} + run: | + (which ctags-universal || which ctags) || (echo "Error: Neither ctags-universal nor ctags found in PATH" && exit 1) + python3 ./.github/scripts/get_affected.py --debug --component ${{ steps.changed-files.outputs.all_changed_files }} > affected_examples.txt + + - name: Upload affected examples + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: affected_examples + path: affected_examples.txt + if-no-files-found: error + + - name: Upload debug artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: get_affected_debug + path: | + ctags_defs_by_qname.json + ctags_header_to_qnames.json + ctags_tags.jsonl + dependencies.json + dependencies_reverse.json + if-no-files-found: warn + + - name: Get Matrix Combinations id: set-matrix run: | - # Default values - idf_ver="release-v5.3,release-v5.4,release-v5.5" - idf_targets="esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + # Define version-specific target configurations + get_targets_for_version() { + case "$1" in + "release-v5.3") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + ;; + "release-v5.4") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + ;; + "release-v5.5") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c5,esp32c6,esp32c61,esp32h2,esp32p4" + ;; + *) + echo "" + ;; + esac + } - # Override with inputs if provided + # Default versions if not provided via inputs + DEFAULT_VERSIONS="release-v5.3,release-v5.4,release-v5.5" + + # Use inputs if provided, otherwise use defaults if [[ -n "${{ inputs.idf_ver }}" ]]; then - idf_ver="${{ inputs.idf_ver }}" - fi - if [[ -n "${{ inputs.idf_targets }}" ]]; then - idf_targets="${{ inputs.idf_targets }}" + VERSIONS="${{ inputs.idf_ver }}" + else + VERSIONS="$DEFAULT_VERSIONS" fi - # Convert comma-separated strings to JSON arrays using a more robust method - idf_ver_json=$(printf '%s\n' "$idf_ver" | tr ',' '\n' | jq -R . | jq -s . | jq -c .) - idf_targets_json=$(printf '%s\n' "$idf_targets" | tr ',' '\n' | jq -R . | jq -s . | jq -c .) + # Generate matrix combinations + echo '{"include": [' > matrix.json + first=true + IFS=',' read -ra VERSION_ARRAY <<< "$VERSIONS" + + for version in "${VERSION_ARRAY[@]}"; do + # Trim whitespace + version=$(echo "$version" | xargs) - # Debug: Print the JSON for verification - echo "Debug - idf_ver_json: $idf_ver_json" - echo "Debug - idf_targets_json: $idf_targets_json" + # Get targets for this version + if [[ -n "${{ inputs.idf_targets }}" ]]; then + # Use provided targets for all versions + targets="${{ inputs.idf_targets }}" + else + # Use version-specific targets + targets=$(get_targets_for_version "$version") + fi - # Set outputs - ensure no extra whitespace - printf "idf_ver=%s\n" "$idf_ver_json" >> $GITHUB_OUTPUT - printf "idf_target=%s\n" "$idf_targets_json" >> $GITHUB_OUTPUT + if [[ -n "$targets" ]]; then + IFS=',' read -ra TARGET_ARRAY <<< "$targets" + for target in "${TARGET_ARRAY[@]}"; do + # Trim whitespace + target=$(echo "$target" | xargs) + + if [ "$first" = true ]; then + first=false + else + echo ',' >> matrix.json + fi + echo "{\"idf_ver\": \"$version\", \"idf_target\": \"$target\"}" >> matrix.json + done + fi + done + echo ']}' >> matrix.json + + # Debug: Print the matrix for verification + echo "Debug - Generated matrix:" + cat matrix.json | jq . + + # Set output + printf "matrix=%s\n" "$(cat matrix.json | jq -c .)" >> $GITHUB_OUTPUT build-esp-idf-component: name: Build IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }} runs-on: ubuntu-latest needs: set-matrix + if: ${{ needs.set-matrix.outputs.should_build == '1' }} strategy: fail-fast: false - matrix: - # The version names here correspond to the versions of espressif/idf Docker image. - # See https://hub.docker.com/r/espressif/idf/tags and - # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html - # for details. - idf_ver: ${{ fromJson(needs.set-matrix.outputs.idf_ver) }} - idf_target: ${{ fromJson(needs.set-matrix.outputs.idf_target) }} + matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }} container: espressif/idf:${{ matrix.idf_ver }} steps: - name: Check out arduino-esp32 as a component @@ -114,16 +202,30 @@ jobs: submodules: recursive path: components/arduino-esp32 + # Need to install jq in the container to be able to use it in the script - name: Setup jq uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 + - name: Setup yq + run: | + YQ_VERSION="v4.48.1" + YQ_BINARY=yq_linux_amd64 + wget -q https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY} -O /usr/bin/yq + chmod +x /usr/bin/yq + yq --version + + - name: Download affected examples + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + name: affected_examples + - name: Build env: IDF_TARGET: ${{ matrix.idf_target }} shell: bash run: | chmod a+x ./components/arduino-esp32/.github/scripts/* - ./components/arduino-esp32/.github/scripts/on-push-idf.sh + ./components/arduino-esp32/.github/scripts/on-push-idf.sh affected_examples.txt - name: Upload generated sdkconfig files for debugging uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 diff --git a/.github/workflows/build_py_tools.yml b/.github/workflows/build_py_tools.yml index bbb36589c84..e22d8df5eff 100644 --- a/.github/workflows/build_py_tools.yml +++ b/.github/workflows/build_py_tools.yml @@ -9,6 +9,10 @@ on: - "tools/gen_esp32part.py" - "tools/gen_insights_package.py" +permissions: + contents: write + pull-requests: read + jobs: find-changed-tools: name: Check if tools have been changed diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9b2c6bccab1..26bd868c190 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -15,6 +15,12 @@ on: - ".github/workflows/*.yml" - ".github/workflows/*.yaml" +permissions: + actions: read + contents: read + pull-requests: read + security-events: write + jobs: codeql-analysis: name: CodeQL ${{ matrix.language }} analysis diff --git a/.github/workflows/docs_build.yml b/.github/workflows/docs_build.yml index d9b9f160228..5253c166f85 100644 --- a/.github/workflows/docs_build.yml +++ b/.github/workflows/docs_build.yml @@ -13,6 +13,9 @@ on: - "docs/**" - ".github/workflows/docs_build.yml" +permissions: + contents: read + jobs: build-docs: name: Build ESP-Docs diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 9f45e35aef8..01eb2b773dc 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -13,9 +13,13 @@ on: - "docs/**" - ".github/workflows/docs_deploy.yml" +permissions: + contents: read + jobs: deploy-prod-docs: name: Deploy Documentation on Production + if: github.repository == 'espressif/arduino-esp32' runs-on: ubuntu-22.04 defaults: run: diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 60795229eff..5dcb46dc36a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -10,6 +10,10 @@ on: - ".github/scripts/on-pages.sh" - ".github/workflows/gh-pages.yml" +permissions: + contents: write + pages: write + jobs: build-pages: name: Build GitHub Pages diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 0cb50842e5d..8af0992ebea 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -13,6 +13,11 @@ concurrency: group: libs-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: write + pull-requests: read + pages: write + env: # It's convenient to set variables for values used multiple times in the workflow SKETCHES_REPORTS_PATH: libraries-report diff --git a/.github/workflows/pre-commit-status.yml b/.github/workflows/pre-commit-status.yml index c7be9f8d352..afd52fdcb5e 100644 --- a/.github/workflows/pre-commit-status.yml +++ b/.github/workflows/pre-commit-status.yml @@ -9,6 +9,7 @@ on: permissions: statuses: write + pull-requests: write jobs: report-success: @@ -62,3 +63,92 @@ jobs: target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' })).data; core.info(`${name} is ${state}`); + + manage-labels: + name: Manage PR labels + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Download and Extract Artifacts + uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 # v9 + continue-on-error: true + with: + run_id: ${{ github.event.workflow_run.id }} + name: pr-artifacts + path: ./pr-artifacts + + - name: Get PR information + id: pr-info + run: | + if [ -f "./pr-artifacts/pr_number.txt" ]; then + pr_number=$(cat ./pr-artifacts/pr_number.txt | tr -cd '[:digit:]') + pre_commit_outcome=$(cat ./pr-artifacts/pre_commit_outcome.txt | tr -cd '[:alpha:]_') + pending_commit=$(cat ./pr-artifacts/pending_commit.txt | tr -cd '[:digit:]') + has_retrigger_label=$(cat ./pr-artifacts/has_retrigger_label.txt | tr -cd '[:alpha:]') + + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + echo "pre_commit_outcome=$pre_commit_outcome" >> $GITHUB_OUTPUT + echo "pending_commit=$pending_commit" >> $GITHUB_OUTPUT + echo "has_retrigger_label=$has_retrigger_label" >> $GITHUB_OUTPUT + echo "artifacts_found=true" >> $GITHUB_OUTPUT + + echo "PR number: $pr_number" + echo "Pre-commit outcome: $pre_commit_outcome" + echo "Pending commit: $pending_commit" + echo "Has retrigger label: $has_retrigger_label" + else + echo "No PR artifacts found" + echo "artifacts_found=false" >> $GITHUB_OUTPUT + fi + + - name: Remove re-trigger label if it was present + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.has_retrigger_label == 'true' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label 'Re-trigger Pre-commit Hooks' + env: + GH_TOKEN: ${{ github.token }} + + - name: Add label if pre-commit fixes are required + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'failure' && + steps.pr-info.outputs.pending_commit == '0' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --add-label 'Status: Pre-commit fixes required ⚠️' + env: + GH_TOKEN: ${{ github.token }} + + - name: Remove label if pre-commit was successful + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'success' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label 'Status: Pre-commit fixes required ⚠️' + env: + GH_TOKEN: ${{ github.token }} + + - name: Comment on PR about pre-commit failures + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'failure' && + steps.pr-info.outputs.pending_commit == '0' + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 + with: + pr-number: ${{ steps.pr-info.outputs.pr_number }} + message: | + ## ⚠️ Pre-commit Hooks Failed + + Some pre-commit hooks failed and require manual fixes. Please see the detailed error report below. + + **What to do:** + 1. 📋 [**View the detailed error report**](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) to see which hooks failed + 2. 🔧 Fix the issues locally in your code + 3. 💾 Commit and push your changes + 4. 🔄 The hooks will run again automatically + + **Need help?** Ask in the comments below. diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index a3b858dd0fb..30bbc3c3e41 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,6 +12,10 @@ concurrency: group: pre-commit-${{github.event.pull_request.number || github.ref}} cancel-in-progress: true +permissions: + contents: read + pull-requests: write + jobs: lint: if: | @@ -27,12 +31,6 @@ jobs: with: fetch-depth: 2 - - name: Remove Label - if: contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit Hooks') - run: gh pr edit ${{ github.event.number }} --remove-label 'Re-trigger Pre-commit Hooks' - env: - GH_TOKEN: ${{ github.token }} - - name: Set up Python 3 uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 with: @@ -61,6 +59,7 @@ jobs: uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - name: Run pre-commit hooks in changed files + id: pre-commit run: pre-commit run --color=always --show-diff-on-failure --files ${{ steps.changed-files.outputs.all_changed_files }} - name: Save pre-commit cache @@ -78,3 +77,21 @@ jobs: if: ${{ always() && github.event_name == 'pull_request' }} with: msg: "ci(pre-commit): Apply automatic fixes" + + - name: Save workflow information for labeling + if: ${{ always() && github.event_name == 'pull_request' }} + run: | + mkdir -p ./pr-artifacts + echo "${{ github.event.number }}" > ./pr-artifacts/pr_number.txt + echo "${{ steps.pre-commit.outcome }}" > ./pr-artifacts/pre_commit_outcome.txt + echo "${{ steps.pre-commit.outputs.pending_commit }}" > ./pr-artifacts/pending_commit.txt + echo "${{ contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit Hooks') }}" > ./pr-artifacts/has_retrigger_label.txt + + - name: Upload PR artifacts + if: ${{ always() && github.event_name == 'pull_request' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: pr-artifacts + path: ./pr-artifacts/ + retention-days: 1 + diff --git a/.github/workflows/publishlib.yml b/.github/workflows/publishlib.yml index 0e1c3f64afd..f97a2e3b5f5 100644 --- a/.github/workflows/publishlib.yml +++ b/.github/workflows/publishlib.yml @@ -12,6 +12,10 @@ env: SKETCHES_REPORTS_PATH: artifacts/libraries-report GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: read + pull-requests: write + jobs: lib-test-results: name: External Libraries Test Results diff --git a/.github/workflows/publishsizes-2.x.yml b/.github/workflows/publishsizes-2.x.yml index 738e215bc3f..f912445e622 100644 --- a/.github/workflows/publishsizes-2.x.yml +++ b/.github/workflows/publishsizes-2.x.yml @@ -9,6 +9,10 @@ env: RESULT_SIZES_TEST_FILE: SIZES_TEST.md GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: write + pull-requests: write + jobs: sizes-test-results: name: Sizes Comparison Results diff --git a/.github/workflows/publishsizes.yml b/.github/workflows/publishsizes.yml index fad2418668c..611ee741efa 100644 --- a/.github/workflows/publishsizes.yml +++ b/.github/workflows/publishsizes.yml @@ -12,6 +12,11 @@ env: SKETCHES_REPORTS_PATH: artifacts/sizes-report GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: read + pull-requests: write + pages: write + jobs: sizes-test-results: name: Sizes Comparison Results diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 48530e30bc9..0dd76135dd9 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -29,17 +29,15 @@ on: - "libraries/**/*.c" - "libraries/**/*.h" - "libraries/**/*.ino" - - "libraries/**/ci.json" + - "libraries/**/ci.yml" - "package/**" - "tools/get.*" - - "platform.txt" - - "programmers.txt" - "package.json" - ".github/workflows/push.yml" - ".github/scripts/install-*" - ".github/scripts/on-push.sh" - - ".github/scripts/set_push_chunks.sh" - ".github/scripts/sketch_utils.sh" + - ".github/scripts/get_affected.py" - "variants/esp32/**" - "variants/esp32c3/**" - "variants/esp32c5/**" @@ -51,11 +49,18 @@ on: - "!*.md" - "!*.txt" - "!*.properties" + - "platform.txt" + - "programmers.txt" concurrency: group: build-${{github.event.pull_request.number || github.ref}} cancel-in-progress: true +permissions: + contents: write + pull-requests: read + pages: write + env: MAX_CHUNKS: 15 @@ -65,12 +70,18 @@ jobs: runs-on: ubuntu-latest if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} outputs: - build_all: ${{ steps.set-chunks.outputs.build_all }} - build_libraries: ${{ steps.set-chunks.outputs.build_libraries }} - build_static_sketches: ${{ steps.set-chunks.outputs.build_static_sketches }} + should_build: ${{ steps.set-chunks.outputs.should_build }} + recompile_preset: ${{ steps.set-chunks.outputs.recompile_preset }} chunk_count: ${{ steps.set-chunks.outputs.chunk_count }} chunks: ${{ steps.set-chunks.outputs.chunks }} steps: + - name: Install universal-ctags + uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3 + with: + packages: libicu74 libjansson4 libxml2 libyaml-0-2 universal-ctags + version: 1 + execute_install_scripts: true + - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -79,60 +90,39 @@ jobs: - name: Get changed files id: changed-files uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - with: - files_yaml: | - core: - - '.github/**' - - 'cores/**' - - 'package/**' - - 'tools/**' - - 'platform.txt' - - 'programmers.txt' - - "variants/**" - libraries: - - 'libraries/**/examples/**' - - 'libraries/**/src/**' - networking: - - 'libraries/Network/src/**' - fs: - - 'libraries/FS/src/**' - static_sketches: - - 'libraries/NetworkClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino' - - 'libraries/BLE/examples/Server/Server.ino' - - 'libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino' - - 'libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino' - - 'libraries/NetworkClientSecure/src/**' - - 'libraries/BLE/src/**' - - 'libraries/Insights/src/**' - name: Set chunks id: set-chunks env: - LIB_FILES: ${{ steps.changed-files.outputs.libraries_all_changed_files }} IS_PR: ${{ github.event_name == 'pull_request' }} MAX_CHUNKS: ${{ env.MAX_CHUNKS }} - BUILD_LIBRARIES: ${{ steps.changed-files.outputs.libraries_any_changed == 'true' }} - BUILD_STATIC_SKETCHES: ${{ steps.changed-files.outputs.static_sketches_any_changed == 'true' }} - FS_CHANGED: ${{ steps.changed-files.outputs.fs_any_changed == 'true' }} - NETWORKING_CHANGED: ${{ steps.changed-files.outputs.networking_any_changed == 'true' }} - CORE_CHANGED: ${{ steps.changed-files.outputs.core_any_changed == 'true' }} - LIB_CHANGED: ${{ steps.changed-files.outputs.libraries_any_changed == 'true' }} run: | - bash ./.github/scripts/set_push_chunks.sh + (which ctags-universal || which ctags) || (echo "Error: Neither ctags-universal nor ctags found in PATH" && exit 1) + python3 ./.github/scripts/get_affected.py --debug ${{ steps.changed-files.outputs.all_changed_files }} > affected_sketches.txt - - name: Upload sketches found - if: ${{ steps.set-chunks.outputs.build_all == 'false' && steps.set-chunks.outputs.build_libraries == 'true' }} + - name: Upload affected sketches uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: sketches_found - path: sketches_found.txt - overwrite: true + name: affected_sketches + path: affected_sketches.txt if-no-files-found: error + - name: Upload debug artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: get_affected_debug + path: | + ctags_defs_by_qname.json + ctags_header_to_qnames.json + ctags_tags.jsonl + dependencies.json + dependencies_reverse.json + if-no-files-found: warn + # Ubuntu build-arduino-linux: name: Arduino ${{ matrix.chunk }} on ubuntu-latest - if: ${{ needs.gen-chunks.outputs.build_all == 'true' || needs.gen-chunks.outputs.build_libraries == 'true' }} + if: ${{ needs.gen-chunks.outputs.should_build == '1' }} needs: gen-chunks runs-on: ubuntu-latest strategy: @@ -169,19 +159,13 @@ jobs: echo "LOG_LEVEL=none" >> $GITHUB_ENV fi - - name: Build all sketches - if: ${{ needs.gen-chunks.outputs.build_all == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ env.MAX_CHUNKS }} 1 ${{ env.LOG_LEVEL }} - - - name: Download sketches found - if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} + - name: Download affected sketches uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: - name: sketches_found + name: affected_sketches - name: Build selected sketches - if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 ${{ env.LOG_LEVEL }} sketches_found.txt + run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 ${{ env.LOG_LEVEL }} affected_sketches.txt #Upload cli compile json as artifact - name: Upload cli compile json @@ -195,7 +179,7 @@ jobs: build-arduino-win-mac: name: Arduino on ${{ matrix.os }} needs: gen-chunks - if: ${{ needs.gen-chunks.outputs.build_all == 'true' || needs.gen-chunks.outputs.build_static_sketches == 'true' }} + if: ${{ needs.gen-chunks.outputs.recompile_preset == '1' }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -204,9 +188,18 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 with: python-version: "3.x" + + # Already installed by default in MacOS and Linux + - name: Install yq (Windows) + if: matrix.os == 'windows-latest' + run: | + choco install yq -y + yq --version + - name: Build Sketches run: bash ./.github/scripts/on-push.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b23c80c49a..f8da07c1957 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: release: types: published +permissions: + contents: write + pages: write + jobs: build: name: Publish Release @@ -13,6 +17,8 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + token: ${{ secrets.TOOLS_UPLOAD_PAT }} + ref: ${{ github.event.release.target_commitish }} fetch-depth: 0 - name: Set up Python @@ -28,5 +34,47 @@ jobs: - name: Build Release env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.TOOLS_UPLOAD_PAT }} run: bash ./.github/scripts/on-release.sh + + - name: Upload hosted binaries + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: hosted + if-no-files-found: ignore + path: ${{ github.workspace }}/hosted + + upload-hosted-binaries: + name: Upload hosted binaries + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages branch + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: gh-pages + + - name: Download hosted binaries + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + name: hosted + path: ${{ github.workspace }}/hosted-latest + + - name: Copy hosted binaries to proper directory and commit + env: + GITHUB_TOKEN: ${{ secrets.TOOLS_UPLOAD_PAT }} + run: | + # Create hosted directory if it doesn't exist + mkdir -p ${{ github.workspace }}/hosted + + # Copy hosted binaries to proper directory without overwriting existing files + cp --update=none ${{ github.workspace }}/hosted-latest/*.bin ${{ github.workspace }}/hosted/ + + # Commit the changes + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add hosted/*.bin + if ! git diff --cached --quiet; then + git commit -m "Add new esp-hosted slave binaries" + git push origin HEAD:gh-pages + fi diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 058d9a3a793..6807f108b49 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -4,9 +4,8 @@ # As the Wokwi tests require access to secrets, they are run in a separate workflow. # We need to ensure that the artifacts from previous tests in the chain are propagated for publishing the results. # This is the current trigger sequence for the tests: -# tests.yml -> tests_wokwi.yml -> tests_results.yml +# tests.yml -> tests_hw_wokwi.yml -> tests_results.yml # ⌙> tests_build.yml -# ⌙> tests_hw.yml # ⌙> tests_qemu.yml name: Runtime Tests @@ -28,12 +27,16 @@ on: - "!*.md" - "!*.properties" schedule: - - cron: "0 2 * * *" + - cron: "0 0 * * *" concurrency: group: tests-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + pull-requests: read + jobs: push-event-file: name: Push event file @@ -50,10 +53,10 @@ jobs: runs-on: ubuntu-latest outputs: build-types: ${{ steps.set-matrix.outputs.build-types }} - hw-types: ${{ steps.set-matrix.outputs.hw-types }} - wokwi-types: ${{ steps.set-matrix.outputs.wokwi-types }} + build-targets: ${{ steps.set-matrix.outputs.build-targets }} + qemu-enabled: ${{ steps.set-matrix.outputs.qemu-enabled }} qemu-types: ${{ steps.set-matrix.outputs.qemu-types }} - targets: ${{ steps.set-matrix.outputs.targets }} + qemu-targets: ${{ steps.set-matrix.outputs.qemu-targets }} env: IS_PR: ${{ github.event.pull_request.number != null }} PERFORMANCE_ENABLED: ${{ contains(github.event.pull_request.labels.*.name, 'perf_test') }} @@ -80,41 +83,23 @@ jobs: strategy: matrix: type: ${{ fromJson(needs.gen-matrix.outputs.build-types) }} - chip: ${{ fromJson(needs.gen-matrix.outputs.targets) }} - with: - type: ${{ matrix.type }} - chip: ${{ matrix.chip }} - - call-hardware-tests: - name: Hardware - uses: ./.github/workflows/tests_hw.yml - needs: [gen-matrix, call-build-tests] - if: | - github.repository == 'espressif/arduino-esp32' && - (github.event_name != 'pull_request' || - contains(github.event.pull_request.labels.*.name, 'hil_test')) - strategy: - fail-fast: false - matrix: - type: ${{ fromJson(needs.gen-matrix.outputs.hw-types) }} - chip: ${{ fromJson(needs.gen-matrix.outputs.targets) }} + chip: ${{ fromJson(needs.gen-matrix.outputs.build-targets) }} with: type: ${{ matrix.type }} chip: ${{ matrix.chip }} - # This job is disabled for now call-qemu-tests: name: QEMU uses: ./.github/workflows/tests_qemu.yml needs: [gen-matrix, call-build-tests] - if: false + if: ${{ needs.gen-matrix.outputs.qemu-enabled == 'true' }} strategy: fail-fast: false matrix: type: ${{ fromJson(needs.gen-matrix.outputs.qemu-types) }} - chip: ["esp32", "esp32c3"] + chip: ${{ fromJson(needs.gen-matrix.outputs.qemu-targets) }} with: type: ${{ matrix.type }} chip: ${{ matrix.chip }} - # Wokwi tests are run after this workflow as it needs access to secrets + # Hardware and Wokwi tests are run after this workflow as they need access to secrets diff --git a/.github/workflows/tests_build.yml b/.github/workflows/tests_build.yml index ac1f40644ed..cd28cc9afbf 100644 --- a/.github/workflows/tests_build.yml +++ b/.github/workflows/tests_build.yml @@ -12,6 +12,9 @@ on: description: "Chip to build tests for" required: true +permissions: + contents: read + jobs: build-tests: name: Build ${{ inputs.type }} tests for ${{ inputs.chip }} @@ -24,12 +27,13 @@ jobs: if: github.event.pull_request.number != null uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-bin + key: test-${{ env.id }}-bin path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.elf ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.json ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/sdkconfig + ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/ci.yml - name: Evaluate if tests should be built id: check-build @@ -71,20 +75,22 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-build.outputs.enabled == 'true' && github.event.pull_request.number != null with: - key: tests-${{ env.id }}-bin + key: test-${{ env.id }}-bin path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.elf ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.json ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/sdkconfig + ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/ci.yml - name: Upload ${{ inputs.chip }} ${{ inputs.type }} binaries as artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} + name: test-bin-${{ inputs.chip }}-${{ inputs.type }} overwrite: true path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.elf ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.json ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/sdkconfig + ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/ci.yml diff --git a/.github/workflows/tests_hw.yml b/.github/workflows/tests_hw.yml deleted file mode 100644 index 6f5fc67f7b9..00000000000 --- a/.github/workflows/tests_hw.yml +++ /dev/null @@ -1,118 +0,0 @@ -name: Hardware tests - -on: - workflow_call: - inputs: - type: - type: string - description: "Type of tests to run" - required: true - chip: - type: string - description: "Chip to run tests for" - required: true - -env: - DEBIAN_FRONTEND: noninteractive - -defaults: - run: - shell: bash - -jobs: - hardware-test: - name: Hardware ${{ inputs.chip }} ${{ inputs.type }} tests - runs-on: ["arduino", "${{ inputs.chip }}"] - env: - id: ${{ github.event.pull_request.number || github.ref }}-${{ github.event.pull_request.head.sha || github.sha }}-${{ inputs.chip }}-${{ inputs.type }} - container: - image: python:3.10.1-bullseye - options: --privileged --device-cgroup-rule="c 188:* rmw" --device-cgroup-rule="c 166:* rmw" - steps: - - name: Clean workspace - run: | - rm -rf ./* - rm -rf ~/.arduino/tests - - - name: Check if already passed - id: cache-results - if: github.event.pull_request.number != null - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - key: tests-${{ env.id }}-results-hw - path: | - tests/**/*.xml - tests/**/result_*.json - - - name: Evaluate if tests should be run - id: check-tests - run: | - cache_exists=${{ steps.cache-results.outputs.cache-hit == 'true' }} - enabled=true - - if [[ $cache_exists == 'true' ]]; then - echo "Already ran, skipping" - enabled=false - fi - - echo "enabled=$enabled" >> $GITHUB_OUTPUT - - - name: Checkout user repository - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - sparse-checkout: | - * - - # setup-python currently only works on ubuntu images - # - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 - # if: ${{ steps.check-tests.outputs.enabled == 'true' }} - # with: - # cache-dependency-path: tests/requirements.txt - # cache: 'pip' - # python-version: '3.10.1' - - - name: Install dependencies - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - run: | - pip install -U pip - pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi - apt update - apt install -y jq - - - name: Get binaries - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} - path: | - ~/.arduino/tests/${{ inputs.chip }} - - - name: List binaries - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - run: | - ls -laR ~/.arduino/tests - - - name: Run Tests - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - run: | - bash .github/scripts/tests_run.sh -c -type ${{ inputs.type }} -t ${{ inputs.chip }} -i 0 -m 1 -e - - - name: Upload ${{ inputs.chip }} ${{ inputs.type }} hardware results as cache - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - if: steps.check-tests.outputs.enabled == 'true' && github.event.pull_request.number != null - with: - key: tests-${{ env.id }}-results-hw - path: | - tests/**/*.xml - tests/**/result_*.json - - - name: Upload ${{ inputs.chip }} ${{ inputs.type }} hardware results as artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: tests-results-hw-${{ inputs.chip }}-${{ inputs.type }} - overwrite: true - path: | - tests/**/*.xml - tests/**/result_*.json diff --git a/.github/workflows/tests_hw_wokwi.yml b/.github/workflows/tests_hw_wokwi.yml new file mode 100644 index 00000000000..e7f7d1e5f29 --- /dev/null +++ b/.github/workflows/tests_hw_wokwi.yml @@ -0,0 +1,590 @@ +name: Hardware and Wokwi tests + +on: + workflow_run: + workflows: ["Runtime Tests"] + types: + - completed + +# No permissions by default +permissions: + contents: read + +env: + TESTS_BRANCH: "master" # Branch that will be checked out to run the tests + +jobs: + get-artifacts: + name: Get required artifacts + runs-on: ubuntu-latest + permissions: + actions: read + statuses: write + outputs: + pr_num: ${{ steps.set-ref.outputs.pr_num }} + ref: ${{ steps.set-ref.outputs.ref }} + base: ${{ steps.set-ref.outputs.base }} + hw_types: ${{ steps.set-ref.outputs.hw_types }} + hw_targets: ${{ steps.set-ref.outputs.hw_targets }} + wokwi_types: ${{ steps.set-ref.outputs.wokwi_types }} + wokwi_targets: ${{ steps.set-ref.outputs.wokwi_targets }} + hw_tests_enabled: ${{ steps.set-ref.outputs.hw_tests_enabled }} + wokwi_tests_enabled: ${{ steps.set-ref.outputs.wokwi_tests_enabled }} + push_time: ${{ steps.set-ref.outputs.push_time }} + steps: + - name: Report pending + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Wokwi (Get artifacts) (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: 'pending', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); + + - name: Download and extract event file + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + name: event_file + path: artifacts/event_file + + - name: Download and extract matrix info + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + name: matrix_info + path: artifacts/matrix_info + + - name: Get info + env: + GITLAB_ACCESS_TOKEN: ${{ secrets.GITLAB_ACCESS_TOKEN }} + WOKWI_CLI_TOKEN: ${{ secrets.WOKWI_CLI_TOKEN }} + id: set-ref + run: | + # Get info and sanitize it to avoid security issues + + pr_num=$(jq -r '.pull_request.number' artifacts/event_file/event.json | tr -cd "[:digit:]") + if [ -z "$pr_num" ] || [ "$pr_num" == "null" ]; then + pr_num="" + fi + + ref=$pr_num + if [ -z "$ref" ] || [ "$ref" == "null" ]; then + ref=${{ github.ref }} + fi + + action=$(jq -r '.action' artifacts/event_file/event.json | tr -cd "[:alpha:]_") + if [ "$action" == "null" ]; then + action="" + fi + + base=$(jq -r '.pull_request.base.ref' artifacts/event_file/event.json | tr -cd "[:alnum:]/_.-") + if [ -z "$base" ] || [ "$base" == "null" ]; then + base=${{ github.ref }} + fi + + if [ -n "$GITLAB_ACCESS_TOKEN" ]; then + hw_tests_enabled="true" + if [[ -n "$pr_num" ]]; then + # This is a PR, check for hil_test label + has_hil_label=$(jq -r '.pull_request.labels[]?.name' artifacts/event_file/event.json 2>/dev/null | grep -q "hil_test" && echo "true" || echo "false") + echo "Has hil_test label: $has_hil_label" + + if [[ "$has_hil_label" != "true" ]]; then + echo "PR does not have hil_test label, hardware tests will be disabled" + hw_tests_enabled="false" + fi + fi + else + echo "GITLAB_ACCESS_TOKEN is not set, hardware tests will be disabled" + hw_tests_enabled="false" + fi + + if [ -n "$WOKWI_CLI_TOKEN" ]; then + wokwi_tests_enabled="true" + else + echo "WOKWI_CLI_TOKEN is not set, wokwi tests will be disabled" + wokwi_tests_enabled="false" + fi + + push_time=$(jq -r '.repository.pushed_at' artifacts/event_file/event.json | tr -cd "[:alnum:]:-") + if [ -z "$push_time" ] || [ "$push_time" == "null" ]; then + push_time="" + fi + + hw_targets=$(jq -c '.hw_targets' artifacts/matrix_info/test_matrix.json | tr -cd "[:alnum:],[]\"") + hw_types=$(jq -c '.hw_types' artifacts/matrix_info/test_matrix.json | tr -cd "[:alpha:],[]\"") + wokwi_targets=$(jq -c '.wokwi_targets' artifacts/matrix_info/test_matrix.json | tr -cd "[:alnum:],[]\"") + wokwi_types=$(jq -c '.wokwi_types' artifacts/matrix_info/test_matrix.json | tr -cd "[:alpha:],[]\"") + qemu_tests_enabled=$(jq -r '.qemu_enabled' artifacts/matrix_info/test_matrix.json | tr -cd "[:alpha:]") + qemu_targets=$(jq -c '.qemu_targets' artifacts/matrix_info/test_matrix.json | tr -cd "[:alnum:],[]\"") + qemu_types=$(jq -c '.qemu_types' artifacts/matrix_info/test_matrix.json | tr -cd "[:alpha:],[]\"") + + echo "base = $base" + echo "hw_targets = $hw_targets" + echo "hw_types = $hw_types" + echo "wokwi_targets = $wokwi_targets" + echo "wokwi_types = $wokwi_types" + echo "qemu_tests_enabled = $qemu_tests_enabled" + echo "qemu_targets = $qemu_targets" + echo "qemu_types = $qemu_types" + echo "pr_num = $pr_num" + echo "hw_tests_enabled = $hw_tests_enabled" + echo "wokwi_tests_enabled = $wokwi_tests_enabled" + echo "push_time = $push_time" + + conclusion="${{ github.event.workflow_run.conclusion }}" + run_id="${{ github.event.workflow_run.id }}" + event="${{ github.event.workflow_run.event }}" + sha="${{ github.event.workflow_run.head_sha || github.sha }}" + + # Create a single JSON file with all workflow run information + cat > artifacts/workflow_info.json <> $GITHUB_OUTPUT + echo "base=$base" >> $GITHUB_OUTPUT + echo "hw_targets=$hw_targets" >> $GITHUB_OUTPUT + echo "hw_types=$hw_types" >> $GITHUB_OUTPUT + echo "wokwi_targets=$wokwi_targets" >> $GITHUB_OUTPUT + echo "wokwi_types=$wokwi_types" >> $GITHUB_OUTPUT + echo "ref=$ref" >> $GITHUB_OUTPUT + echo "hw_tests_enabled=$hw_tests_enabled" >> $GITHUB_OUTPUT + echo "wokwi_tests_enabled=$wokwi_tests_enabled" >> $GITHUB_OUTPUT + echo "push_time=$push_time" >> $GITHUB_OUTPUT + + - name: Download and extract parent QEMU results + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + continue-on-error: true + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + pattern: test-results-qemu-* + merge-multiple: true + path: artifacts/results/qemu + + - name: Upload parent artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: parent-artifacts + path: artifacts + if-no-files-found: error + + - name: Report conclusion + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + if: always() + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Wokwi (Get artifacts) (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: '${{ job.status }}', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); + + hardware-test: + name: Internal Hardware Tests + if: | + (github.event.workflow_run.conclusion == 'success' || + github.event.workflow_run.conclusion == 'failure' || + github.event.workflow_run.conclusion == 'timed_out') && + needs.get-artifacts.outputs.hw_tests_enabled == 'true' + runs-on: ubuntu-latest + needs: get-artifacts + env: + id: ${{ needs.get-artifacts.outputs.ref }}-${{ github.event.workflow_run.head_sha || github.sha }} + permissions: + actions: read + statuses: write + steps: + - name: Report pending + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Internal Hardware Tests (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: 'pending', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); + + - name: Check if already passed + id: get-cache-results + if: needs.get-artifacts.outputs.pr_num + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + key: test-${{ env.id }}-results-hw + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Evaluate if tests should be run + id: check-tests + run: | + cache_exists=${{ steps.get-cache-results.outputs.cache-hit == 'true' }} + enabled=true + + # Check cache first + if [[ $cache_exists == 'true' ]]; then + echo "Already ran, skipping GitLab pipeline trigger" + enabled=false + else + echo "Cache miss, hardware tests will run" + fi + + echo "enabled=$enabled" >> $GITHUB_OUTPUT + + - name: Wait for GitLab sync and prepare variables + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + id: prepare-variables + env: + PUSH_TIME: ${{ needs.get-artifacts.outputs.push_time }} + run: | + # A webhook to sync the repository is sent to GitLab when a commit is pushed to GitHub + # We wait for 10 minutes after the push to GitHub to be safe + + echo "Ensuring GitLab sync has completed before triggering pipeline..." + + # Use push time determined in get-artifacts job + push_time="$PUSH_TIME" + + if [ -n "$push_time" ]; then + echo "Push time: $push_time" + + # Convert push time to epoch + push_epoch=$(date -d "$push_time" +%s 2>/dev/null || echo "") + + if [ -n "$push_epoch" ]; then + current_epoch=$(date +%s) + elapsed_minutes=$(( (current_epoch - push_epoch) / 60 )) + + echo "Elapsed time since push: ${elapsed_minutes} minutes" + + if [ $elapsed_minutes -lt 10 ]; then + wait_time=$(( (10 - elapsed_minutes) * 60 )) + echo "Waiting ${wait_time} seconds for GitLab sync to complete..." + sleep $wait_time + else + echo "GitLab sync should be complete (${elapsed_minutes} minutes elapsed)" + fi + else + echo "Could not parse push timestamp, waiting 60 seconds as fallback..." + sleep 60 + fi + else + echo "Could not determine push time, waiting 60 seconds as fallback..." + sleep 60 + fi + + echo "Proceeding with GitLab pipeline trigger..." + + # Make targets/types comma-separated strings (remove brackets and quotes) + test_types=$(printf '%s' "${{ needs.get-artifacts.outputs.hw_types }}" | sed -e 's/[][]//g' -e 's/"//g') + test_chips=$(printf '%s' "${{ needs.get-artifacts.outputs.hw_targets }}" | sed -e 's/[][]//g' -e 's/"//g') + echo "test_types=$test_types" + echo "test_chips=$test_chips" + + # Expose as step outputs + echo "test_types=$test_types" >> $GITHUB_OUTPUT + echo "test_chips=$test_chips" >> $GITHUB_OUTPUT + + - name: Trigger GitLab Pipeline and Download Artifacts + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + uses: digital-blueprint/gitlab-pipeline-trigger-action@20e77989b24af658ba138a0aa5291bdc657f1505 # v1.3.0 + id: gitlab-trigger + with: + host: ${{ secrets.GITLAB_URL }} + id: ${{ secrets.GITLAB_PROJECT_ID }} + ref: ${{ env.TESTS_BRANCH }} + trigger_token: ${{ secrets.GITLAB_TRIGGER_TOKEN }} + access_token: ${{ secrets.GITLAB_ACCESS_TOKEN }} + download_artifacts: 'true' + download_artifacts_on_failure: 'true' + download_path: './gitlab-artifacts' + variables: >- + { + "TEST_TYPES":"${{ steps.prepare-variables.outputs.test_types }}", + "TEST_CHIPS":"${{ steps.prepare-variables.outputs.test_chips }}", + "PIPELINE_ID":"${{ env.id }}", + "BINARIES_RUN_ID":"${{ github.event.workflow_run.id }}", + "GITHUB_REPOSITORY":"${{ github.repository }}" + } + + - name: Process Downloaded Artifacts + if: ${{ always() && steps.check-tests.outputs.enabled == 'true' }} + run: | + echo "GitLab Pipeline Status: ${{ steps.gitlab-trigger.outputs.status }}" + echo "Artifacts Downloaded: ${{ steps.gitlab-trigger.outputs.artifacts_downloaded }}" + + # Create tests directory structure expected by GitHub caching + mkdir -p tests + + # Process downloaded GitLab artifacts + if [ "${{ steps.gitlab-trigger.outputs.artifacts_downloaded }}" = "true" ]; then + echo "Processing downloaded GitLab artifacts..." + + # Find and copy test result files while preserving directory structure + # The GitLab artifacts have the structure: gitlab-artifacts/job_*/artifacts/tests/... + # We want to preserve the tests/... part of the structure + + for job_dir in ./gitlab-artifacts/job_*; do + if [ -d "$job_dir/artifacts/tests" ]; then + # Merge results into tests/ without failing on non-empty directories + echo "Merging $job_dir/artifacts/tests/ into tests/" + cp -a "$job_dir/artifacts/tests/." tests/ + fi + done + + echo "Test results found:" + ls -laR tests/ || echo "No test results found" + else + echo "No artifacts were downloaded from GitLab" + fi + + - name: Upload hardware results as cache + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + if: steps.check-tests.outputs.enabled == 'true' && needs.get-artifacts.outputs.pr_num + with: + key: test-${{ env.id }}-results-hw + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Upload hardware results as artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: always() + with: + name: test-results-hw + overwrite: true + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Report conclusion + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + if: always() + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Internal Hardware Tests (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: '${{ job.status }}', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); + + wokwi-test: + name: Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests + if: | + (github.event.workflow_run.conclusion == 'success' || + github.event.workflow_run.conclusion == 'failure' || + github.event.workflow_run.conclusion == 'timed_out') && + needs.get-artifacts.outputs.wokwi_tests_enabled == 'true' + runs-on: ubuntu-latest + needs: get-artifacts + env: + id: ${{ needs.get-artifacts.outputs.ref }}-${{ github.event.workflow_run.head_sha || github.sha }}-${{ matrix.chip }}-${{ matrix.type }} + permissions: + actions: read + statuses: write + strategy: + fail-fast: false + matrix: + type: ${{ fromJson(needs.get-artifacts.outputs.wokwi_types) }} + chip: ${{ fromJson(needs.get-artifacts.outputs.wokwi_targets) }} + steps: + - name: Report pending + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Wokwi (${{ matrix.type }}, ${{ matrix.chip }}) / Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: 'pending', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); + + - name: Check if already passed + id: get-cache-results + if: needs.get-artifacts.outputs.pr_num + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + with: + key: test-${{ env.id }}-results-wokwi + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Evaluate if tests should be run + id: check-tests + run: | + cache_exists=${{ steps.get-cache-results.outputs.cache-hit == 'true' }} + enabled=true + + if [[ $cache_exists == 'true' ]]; then + echo "Already ran, skipping" + enabled=false + fi + + echo "enabled=$enabled" >> $GITHUB_OUTPUT + + # Note that changes to the workflows and tests will only be picked up after the PR is merged + # DO NOT CHECKOUT THE USER'S REPOSITORY IN THIS WORKFLOW. IT HAS HIGH SECURITY RISKS. + - name: Checkout repository + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ needs.get-artifacts.outputs.base || github.ref }} + + - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + with: + cache-dependency-path: tests/requirements.txt + cache: "pip" + python-version: "3.x" + + - name: Install dependencies + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + run: | + pip install -U pip + pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi + + - name: Wokwi CI Server + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + uses: wokwi/wokwi-ci-server-action@a6fabb5a49e080158c7a1d121ea5b789536a82c3 # v1 + + - name: Get binaries + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + name: test-bin-${{ matrix.chip }}-${{ matrix.type }} + path: | + ~/.arduino/tests/${{ matrix.chip }} + + - name: Run Tests + if: ${{ steps.check-tests.outputs.enabled == 'true' }} + env: + WOKWI_CLI_TOKEN: ${{ secrets.WOKWI_CLI_TOKEN }} + WOKWI_WIFI_SSID: "Wokwi-GUEST" + # The Wokwi Wi-Fi does not have a password, so we use an empty string + WOKWI_WIFI_PASSWORD: "" + run: | + bash .github/scripts/tests_run.sh -c -type "${{ matrix.type }}" -t "${{ matrix.chip }}" -i 0 -m 1 -W -wifi-ssid "${{ env.WOKWI_WIFI_SSID }}" -wifi-password "${{ env.WOKWI_WIFI_PASSWORD }}" + + - name: Upload ${{ matrix.chip }} ${{ matrix.type }} Wokwi results as cache + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 + if: steps.check-tests.outputs.enabled == 'true' && needs.get-artifacts.outputs.pr_num + with: + key: test-${{ env.id }}-results-wokwi + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Upload ${{ matrix.chip }} ${{ matrix.type }} Wokwi results as artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: always() + with: + name: test-results-wokwi-${{ matrix.chip }}-${{ matrix.type }} + overwrite: true + path: | + tests/**/*.xml + tests/**/result_*.json + + - name: Report conclusion + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + if: always() + with: + script: | + const owner = '${{ github.repository_owner }}'; + const repo = '${{ github.repository }}'.split('/')[1]; + const sha = '${{ github.event.workflow_run.head_sha }}'; + core.debug(`owner: ${owner}`); + core.debug(`repo: ${repo}`); + core.debug(`sha: ${sha}`); + const { context: name, state } = (await github.rest.repos.createCommitStatus({ + context: 'Runtime Tests / Wokwi (${{ matrix.type }}, ${{ matrix.chip }}) / Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests (${{ github.event.workflow_run.event }} -> workflow_run)', + owner: owner, + repo: repo, + sha: sha, + state: '${{ job.status }}', + target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' + })).data; + core.info(`${name} is ${state}`); diff --git a/.github/workflows/tests_qemu.yml b/.github/workflows/tests_qemu.yml index fa3f874cbbb..9375f802073 100644 --- a/.github/workflows/tests_qemu.yml +++ b/.github/workflows/tests_qemu.yml @@ -10,6 +10,9 @@ on: required: true type: string +permissions: + contents: read + jobs: qemu-test: name: QEMU ${{ inputs.chip }} ${{ inputs.type }} tests @@ -23,7 +26,7 @@ jobs: if: github.event.pull_request.number != null uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-results-qemu + key: test-${{ env.id }}-results-qemu path: | tests/**/*.xml tests/**/result_*.json @@ -115,7 +118,7 @@ jobs: if: ${{ steps.check-tests.outputs.enabled == 'true' }} uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} + name: test-bin-${{ inputs.chip }}-${{ inputs.type }} path: | ~/.arduino/tests/${{ inputs.chip }} @@ -127,7 +130,7 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-tests.outputs.enabled == 'true' && github.event.pull_request.number != null with: - key: tests-${{ env.id }}-results-qemu + key: test-${{ env.id }}-results-qemu path: | tests/**/*.xml tests/**/result_*.json @@ -136,7 +139,7 @@ jobs: uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: tests-results-qemu-${{ inputs.chip }}-${{ inputs.type }} + name: test-results-qemu-${{ inputs.chip }}-${{ inputs.type }} overwrite: true path: | tests/**/*.xml diff --git a/.github/workflows/tests_results.yml b/.github/workflows/tests_results.yml index ebba2a3aa08..7b638a0e972 100644 --- a/.github/workflows/tests_results.yml +++ b/.github/workflows/tests_results.yml @@ -2,16 +2,105 @@ name: Publish and clean test results on: workflow_run: - workflows: ["Wokwi tests"] + workflows: ["Hardware and Wokwi tests"] types: - completed # No permissions by default -permissions: { contents: read } +permissions: + contents: read jobs: + get-artifacts: + name: Get artifacts + runs-on: ubuntu-latest + outputs: + original_event: ${{ steps.get-info.outputs.original_event }} + original_action: ${{ steps.get-info.outputs.original_action }} + original_sha: ${{ steps.get-info.outputs.original_sha }} + original_ref: ${{ steps.get-info.outputs.original_ref }} + original_conclusion: ${{ steps.get-info.outputs.original_conclusion }} + original_run_id: ${{ steps.get-info.outputs.original_run_id }} + hw_tests_enabled: ${{ steps.get-info.outputs.hw_tests_enabled }} + hw_targets: ${{ steps.get-info.outputs.hw_targets }} + hw_types: ${{ steps.get-info.outputs.hw_types }} + wokwi_tests_enabled: ${{ steps.get-info.outputs.wokwi_tests_enabled }} + wokwi_targets: ${{ steps.get-info.outputs.wokwi_targets }} + wokwi_types: ${{ steps.get-info.outputs.wokwi_types }} + qemu_tests_enabled: ${{ steps.get-info.outputs.qemu_tests_enabled }} + qemu_targets: ${{ steps.get-info.outputs.qemu_targets }} + qemu_types: ${{ steps.get-info.outputs.qemu_types }} + steps: + - name: Download and Extract Artifacts + uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 # v9 + with: + run_id: ${{ github.event.workflow_run.id }} + path: ./artifacts + + - name: Get original info + id: get-info + run: | + # Inputs in workflow_info.json are already sanitized and safe to use + + original_event=$(jq -r '.event' ./artifacts/parent-artifacts/workflow_info.json) + original_action=$(jq -r '.action' ./artifacts/parent-artifacts/workflow_info.json) + original_sha=$(jq -r '.sha' ./artifacts/parent-artifacts/workflow_info.json) + original_ref=$(jq -r '.ref' ./artifacts/parent-artifacts/workflow_info.json) + original_conclusion=$(jq -r '.conclusion' ./artifacts/parent-artifacts/workflow_info.json) + original_run_id=$(jq -r '.run_id' ./artifacts/parent-artifacts/workflow_info.json) + + hw_tests_enabled=$(jq -r '.hw_tests_enabled' ./artifacts/parent-artifacts/workflow_info.json) + hw_targets=$(jq -c '.hw_targets' ./artifacts/parent-artifacts/workflow_info.json) + hw_types=$(jq -c '.hw_types' ./artifacts/parent-artifacts/workflow_info.json) + wokwi_tests_enabled=$(jq -r '.wokwi_tests_enabled' ./artifacts/parent-artifacts/workflow_info.json) + wokwi_targets=$(jq -c '.wokwi_targets' ./artifacts/parent-artifacts/workflow_info.json) + wokwi_types=$(jq -c '.wokwi_types' ./artifacts/parent-artifacts/workflow_info.json) + qemu_tests_enabled=$(jq -r '.qemu_tests_enabled' ./artifacts/parent-artifacts/workflow_info.json) + qemu_targets=$(jq -c '.qemu_targets' ./artifacts/parent-artifacts/workflow_info.json) + qemu_types=$(jq -c '.qemu_types' ./artifacts/parent-artifacts/workflow_info.json) + + echo "hw_tests_enabled=$hw_tests_enabled" >> $GITHUB_OUTPUT + echo "hw_targets=$hw_targets" >> $GITHUB_OUTPUT + echo "hw_types=$hw_types" >> $GITHUB_OUTPUT + echo "wokwi_tests_enabled=$wokwi_tests_enabled" >> $GITHUB_OUTPUT + echo "wokwi_targets=$wokwi_targets" >> $GITHUB_OUTPUT + echo "wokwi_types=$wokwi_types" >> $GITHUB_OUTPUT + echo "qemu_tests_enabled=$qemu_tests_enabled" >> $GITHUB_OUTPUT + echo "qemu_targets=$qemu_targets" >> $GITHUB_OUTPUT + echo "qemu_types=$qemu_types" >> $GITHUB_OUTPUT + echo "original_event=$original_event" >> $GITHUB_OUTPUT + echo "original_action=$original_action" >> $GITHUB_OUTPUT + echo "original_sha=$original_sha" >> $GITHUB_OUTPUT + echo "original_ref=$original_ref" >> $GITHUB_OUTPUT + echo "original_conclusion=$original_conclusion" >> $GITHUB_OUTPUT + echo "original_run_id=$original_run_id" >> $GITHUB_OUTPUT + + echo "hw_tests_enabled = $hw_tests_enabled" + echo "hw_targets = $hw_targets" + echo "hw_types = $hw_types" + echo "wokwi_tests_enabled = $wokwi_tests_enabled" + echo "wokwi_targets = $wokwi_targets" + echo "wokwi_types = $wokwi_types" + echo "qemu_tests_enabled = $qemu_tests_enabled" + echo "qemu_targets = $qemu_targets" + echo "qemu_types = $qemu_types" + echo "original_event = $original_event" + echo "original_action = $original_action" + echo "original_sha = $original_sha" + echo "original_ref = $original_ref" + echo "original_conclusion = $original_conclusion" + echo "original_run_id = $original_run_id" + + - name: Print links to other runs + env: + ORIGINAL_RUN_ID: ${{ steps.get-info.outputs.original_run_id }} + run: | + echo "Build and QEMU tests: https://github.com/${{ github.repository }}/actions/runs/$ORIGINAL_RUN_ID" + echo "Hardware and Wokwi tests: https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}" + unit-test-results: name: Unit Test Results + needs: get-artifacts if: | github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' || @@ -34,88 +123,68 @@ jobs: run_id: ${{ github.event.workflow_run.id }} path: ./artifacts - - name: Get original info - run: | - original_event=$(cat ./artifacts/parent-artifacts/event.txt) - original_action=$(cat ./artifacts/parent-artifacts/action.txt) - original_sha=$(cat ./artifacts/parent-artifacts/sha.txt) - original_ref=$(cat ./artifacts/parent-artifacts/ref.txt) - original_conclusion=$(cat ./artifacts/parent-artifacts/conclusion.txt) - original_run_id=$(cat ./artifacts/parent-artifacts/run_id.txt) - - # Sanitize the values to avoid security issues - - # Event: Allow alphabetical characters and underscores - original_event=$(echo "$original_event" | tr -cd '[:alpha:]_') - - # Action: Allow alphabetical characters and underscores - original_action=$(echo "$original_action" | tr -cd '[:alpha:]_') - - # SHA: Allow alphanumeric characters - original_sha=$(echo "$original_sha" | tr -cd '[:alnum:]') - - # Ref: Allow alphanumeric characters, slashes, underscores, dots, and dashes - original_ref=$(echo "$original_ref" | tr -cd '[:alnum:]/_.-') - - # Conclusion: Allow alphabetical characters and underscores - original_conclusion=$(echo "$original_conclusion" | tr -cd '[:alpha:]_') - - # Run ID: Allow numeric characters - original_run_id=$(echo "$original_run_id" | tr -cd '[:digit:]') - - echo "original_event=$original_event" >> $GITHUB_ENV - echo "original_action=$original_action" >> $GITHUB_ENV - echo "original_sha=$original_sha" >> $GITHUB_ENV - echo "original_ref=$original_ref" >> $GITHUB_ENV - echo "original_conclusion=$original_conclusion" >> $GITHUB_ENV - echo "original_run_id=$original_run_id" >> $GITHUB_ENV - - echo "original_event = $original_event" - echo "original_action = $original_action" - echo "original_sha = $original_sha" - echo "original_ref = $original_ref" - echo "original_conclusion = $original_conclusion" - echo "original_run_id = $original_run_id" + - name: Download and Extract Artifacts + uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 # v9 + with: + run_id: ${{ needs.get-artifacts.outputs.original_run_id }} + path: ./build_artifacts - - name: Print links to other runs + - name: Generate JUnit files for missing runs + env: + GH_TOKEN: ${{ github.token }} + HW_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.hw_tests_enabled }} + HW_TARGETS: ${{ needs.get-artifacts.outputs.hw_targets }} + HW_TYPES: ${{ needs.get-artifacts.outputs.hw_types }} + WOKWI_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.wokwi_tests_enabled }} + WOKWI_TARGETS: ${{ needs.get-artifacts.outputs.wokwi_targets }} + WOKWI_TYPES: ${{ needs.get-artifacts.outputs.wokwi_types }} + QEMU_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.qemu_tests_enabled }} + QEMU_TARGETS: ${{ needs.get-artifacts.outputs.qemu_targets }} + QEMU_TYPES: ${{ needs.get-artifacts.outputs.qemu_types }} run: | - echo "Build, Hardware and QEMU tests: https://github.com/${{ github.repository }}/actions/runs/${{ env.original_run_id }}" - echo "Wokwi tests: https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}" + ls -la ./artifacts + ls -la ./build_artifacts + pip3 install pyyaml + wget https://raw.githubusercontent.com/${{ github.repository }}/master/.github/scripts/generate_missing_junits.py -O ./generate_missing_junits.py + python3 ./generate_missing_junits.py ./build_artifacts ./artifacts ./test_errors - name: Publish Unit Test Results + id: publish-test-results uses: EnricoMi/publish-unit-test-result-action@170bf24d20d201b842d7a52403b73ed297e6645b # v2.18.0 with: - commit: ${{ env.original_sha }} + commit: ${{ needs.get-artifacts.outputs.original_sha }} event_file: ./artifacts/parent-artifacts/event_file/event.json - event_name: ${{ env.original_event }} - files: ./artifacts/**/*.xml + event_name: ${{ needs.get-artifacts.outputs.original_event }} + files: | + ./artifacts/**/*.xml + ./test_errors/**/*.xml action_fail: true + action_fail_on_inconclusive: true compare_to_earlier_commit: false json_file: ./unity_results.json json_suite_details: true - name: Upload JSON uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: ${{ always() }} + if: always() with: name: unity_results overwrite: true - path: | - ./unity_results.json - - - name: Fail if tests failed - if: ${{ env.original_conclusion == 'failure' || env.original_conclusion == 'timed_out' || github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'timed_out' }} - run: exit 1 + path: ./unity_results.json - name: Clean up caches if: always() uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + ORIGINAL_REF: ${{ needs.get-artifacts.outputs.original_ref }} + ORIGINAL_EVENT: ${{ needs.get-artifacts.outputs.original_event }} + ORIGINAL_ACTION: ${{ needs.get-artifacts.outputs.original_action }} with: script: | - const ref = process.env.original_ref; - const key_prefix = 'tests-' + ref + '-'; + const ref = process.env.ORIGINAL_REF; + const key_prefix = 'test-' + ref + '-'; - if (process.env.original_event == 'pull_request' && process.env.original_action != 'closed') { + if (process.env.ORIGINAL_EVENT == 'pull_request' && process.env.ORIGINAL_ACTION != 'closed') { console.log('Skipping cache cleanup for open PR'); return; } @@ -141,16 +210,19 @@ jobs: - name: Report conclusion uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 if: always() + env: + ORIGINAL_EVENT: ${{ needs.get-artifacts.outputs.original_event }} + ORIGINAL_SHA: ${{ needs.get-artifacts.outputs.original_sha }} with: script: | const owner = '${{ github.repository_owner }}'; const repo = '${{ github.repository }}'.split('/')[1]; - const sha = process.env.original_sha; + const sha = process.env.ORIGINAL_SHA; core.debug(`owner: ${owner}`); core.debug(`repo: ${repo}`); core.debug(`sha: ${sha}`); const { context: name, state } = (await github.rest.repos.createCommitStatus({ - context: `Runtime Tests / Report results (${process.env.original_event} -> workflow_run -> workflow_run)`, + context: `Runtime Tests / Report results (${process.env.ORIGINAL_EVENT} -> workflow_run -> workflow_run)`, owner: owner, repo: repo, sha: sha, @@ -161,35 +233,72 @@ jobs: core.info(`${name} is ${state}`); - name: Generate report - if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled + if: | + (!cancelled() && + needs.get-artifacts.outputs.original_conclusion != 'cancelled' && + github.event.workflow_run.conclusion != 'cancelled') && + (needs.get-artifacts.outputs.original_event == 'schedule' || + needs.get-artifacts.outputs.original_event == 'workflow_dispatch') env: - REPORT_FILE: ./runtime-tests-results/RUNTIME_TESTS_REPORT.md + HW_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.hw_tests_enabled }} + HW_TARGETS: ${{ needs.get-artifacts.outputs.hw_targets }} + HW_TYPES: ${{ needs.get-artifacts.outputs.hw_types }} + WOKWI_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.wokwi_tests_enabled }} + WOKWI_TARGETS: ${{ needs.get-artifacts.outputs.wokwi_targets }} + WOKWI_TYPES: ${{ needs.get-artifacts.outputs.wokwi_types }} + QEMU_TESTS_ENABLED: ${{ needs.get-artifacts.outputs.qemu_tests_enabled }} + QEMU_TARGETS: ${{ needs.get-artifacts.outputs.qemu_targets }} + QEMU_TYPES: ${{ needs.get-artifacts.outputs.qemu_types }} WOKWI_RUN_ID: ${{ github.event.workflow_run.id }} - BUILD_RUN_ID: ${{ env.original_run_id }} - IS_FAILING: ${{ env.original_conclusion == 'failure' || env.original_conclusion == 'timed_out' || github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'timed_out' || job.status == 'failure' }} + BUILD_RUN_ID: ${{ needs.get-artifacts.outputs.original_run_id }} + RESULTS_URL: ${{ fromJSON( steps.publish-test-results.outputs.json ).check_url }} + RESULTS_RUN_ID: ${{ github.run_id }} + REPORT_FILE: ./runtime-test-results/RUNTIME_TEST_RESULTS.md + IS_FAILING: >- + ${{ + needs.get-artifacts.outputs.original_conclusion == 'failure' || + needs.get-artifacts.outputs.original_conclusion == 'cancelled' || + needs.get-artifacts.outputs.original_conclusion == 'timed_out' || + github.event.workflow_run.conclusion == 'failure' || + github.event.workflow_run.conclusion == 'cancelled' || + github.event.workflow_run.conclusion == 'timed_out' || + job.status == 'failure' + }} run: | rm -rf artifacts $REPORT_FILE - mv -f ./unity_results.json ./runtime-tests-results/unity_results.json + mv -f ./unity_results.json ./runtime-test-results/unity_results.json touch $REPORT_FILE - python3 ./runtime-tests-results/table_generator.py ./runtime-tests-results/unity_results.json >> $REPORT_FILE + wget https://raw.githubusercontent.com/${{ github.repository }}/master/.github/scripts/runtime_table_generator.py -O ./runtime-test-results/runtime_table_generator.py + python3 ./runtime-test-results/runtime_table_generator.py ./runtime-test-results/unity_results.json ${{ needs.get-artifacts.outputs.original_sha }} >> $REPORT_FILE + mv -f ./test_results.json ./runtime-test-results/test_results.json - name: Generate badge - if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled + if: | + (!cancelled() && + needs.get-artifacts.outputs.original_conclusion != 'cancelled' && + github.event.workflow_run.conclusion != 'cancelled') && + (needs.get-artifacts.outputs.original_event == 'schedule' || + needs.get-artifacts.outputs.original_event == 'workflow_dispatch') uses: jaywcjlove/generated-badges@0e078ae4d4bab3777ea4f137de496ab44688f5ad # v1.0.13 with: label: Runtime Tests status: ${{ job.status == 'success' && 'passing' || 'failing' }} - output: runtime-tests-results/badge.svg + output: runtime-test-results/badge.svg color: ${{ job.status == 'success' && 'green' || 'red' }} style: flat - name: Push badge - if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled + if: | + (!cancelled() && + needs.get-artifacts.outputs.original_conclusion != 'cancelled' && + github.event.workflow_run.conclusion != 'cancelled') && + (needs.get-artifacts.outputs.original_event == 'schedule' || + needs.get-artifacts.outputs.original_event == 'workflow_dispatch') run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" if [[ `git status --porcelain` ]]; then - git add --all - git commit -m "Updated runtime tests report" + git add runtime-test-results/RUNTIME_TEST_RESULTS.md runtime-test-results/badge.svg runtime-test-results/test_results.json runtime-test-results/unity_results.json + git commit -m "Updated runtime test results" git push origin HEAD:gh-pages fi diff --git a/.github/workflows/tests_wokwi.yml b/.github/workflows/tests_wokwi.yml deleted file mode 100644 index 03dd64fc0fb..00000000000 --- a/.github/workflows/tests_wokwi.yml +++ /dev/null @@ -1,326 +0,0 @@ -name: Wokwi tests - -on: - workflow_run: - workflows: ["Runtime Tests"] - types: - - completed - -# No permissions by default -permissions: { contents: read } - -env: - WOKWI_TIMEOUT: 600000 # Milliseconds - -jobs: - get-artifacts: - name: Get required artifacts - runs-on: ubuntu-latest - permissions: - actions: read - statuses: write - outputs: - pr_num: ${{ steps.set-ref.outputs.pr_num }} - ref: ${{ steps.set-ref.outputs.ref }} - base: ${{ steps.set-ref.outputs.base }} - targets: ${{ steps.set-ref.outputs.targets }} - types: ${{ steps.set-ref.outputs.types }} - steps: - - name: Report pending - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - const owner = '${{ github.repository_owner }}'; - const repo = '${{ github.repository }}'.split('/')[1]; - const sha = '${{ github.event.workflow_run.head_sha }}'; - core.debug(`owner: ${owner}`); - core.debug(`repo: ${repo}`); - core.debug(`sha: ${sha}`); - const { context: name, state } = (await github.rest.repos.createCommitStatus({ - context: 'Runtime Tests / Wokwi (Get artifacts) (${{ github.event.workflow_run.event }} -> workflow_run)', - owner: owner, - repo: repo, - sha: sha, - state: 'pending', - target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' - })).data; - core.info(`${name} is ${state}`); - - - name: Download and extract event file - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - name: event_file - path: artifacts/event_file - - - name: Download and extract matrix info - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - name: matrix_info - path: artifacts/matrix_info - - - name: Try to read PR number - id: set-ref - run: | - pr_num=$(jq -r '.pull_request.number' artifacts/event_file/event.json | tr -cd "[:digit:]") - if [ -z "$pr_num" ] || [ "$pr_num" == "null" ]; then - pr_num="" - fi - - ref=$pr_num - if [ -z "$ref" ] || [ "$ref" == "null" ]; then - ref=${{ github.ref }} - fi - - action=$(jq -r '.action' artifacts/event_file/event.json | tr -cd "[:alpha:]_") - if [ "$action" == "null" ]; then - action="" - fi - - base=$(jq -r '.pull_request.base.ref' artifacts/event_file/event.json | tr -cd "[:alnum:]/_.-") - if [ -z "$base" ] || [ "$base" == "null" ]; then - base=${{ github.ref }} - fi - - types=$(cat artifacts/matrix_info/wokwi_types.txt | tr -cd "[:alpha:],[]'") - targets=$(cat artifacts/matrix_info/targets.txt | tr -cd "[:alnum:],[]'") - - echo "base = $base" - echo "targets = $targets" - echo "types = $types" - echo "pr_num = $pr_num" - - printf "$ref" >> artifacts/ref.txt - printf "Ref = " - cat artifacts/ref.txt - - printf "${{ github.event.workflow_run.event }}" >> artifacts/event.txt - printf "\nEvent name = " - cat artifacts/event.txt - - printf "${{ github.event.workflow_run.head_sha || github.sha }}" >> artifacts/sha.txt - printf "\nHead SHA = " - cat artifacts/sha.txt - - printf "$action" >> artifacts/action.txt - printf "\nAction = " - cat artifacts/action.txt - - printf "${{ github.event.workflow_run.id }}" >> artifacts/run_id.txt - printf "\nRun ID = " - cat artifacts/run_id.txt - - if [ -z "$ref" ] || [ "$ref" == "null" ]; then - echo "Failed to get PR number or ref" - exit 1 - fi - - conclusion="${{ github.event.workflow_run.conclusion }}" - printf "$conclusion" >> artifacts/conclusion.txt - printf "\nConclusion = " - cat artifacts/conclusion.txt - - echo "pr_num=$pr_num" >> $GITHUB_OUTPUT - echo "base=$base" >> $GITHUB_OUTPUT - echo "targets=$targets" >> $GITHUB_OUTPUT - echo "types=$types" >> $GITHUB_OUTPUT - echo "ref=$ref" >> $GITHUB_OUTPUT - - - name: Download and extract parent hardware results - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - continue-on-error: true - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - pattern: tests-results-hw-* - merge-multiple: true - path: artifacts/results/hw - - - name: Download and extract parent QEMU results - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - continue-on-error: true - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - pattern: tests-results-qemu-* - merge-multiple: true - path: artifacts/results/qemu - - - name: Upload parent artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: parent-artifacts - path: artifacts - if-no-files-found: error - - - name: Report conclusion - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - if: always() - with: - script: | - const owner = '${{ github.repository_owner }}'; - const repo = '${{ github.repository }}'.split('/')[1]; - const sha = '${{ github.event.workflow_run.head_sha }}'; - core.debug(`owner: ${owner}`); - core.debug(`repo: ${repo}`); - core.debug(`sha: ${sha}`); - const { context: name, state } = (await github.rest.repos.createCommitStatus({ - context: 'Runtime Tests / Wokwi (Get artifacts) (${{ github.event.workflow_run.event }} -> workflow_run)', - owner: owner, - repo: repo, - sha: sha, - state: '${{ job.status }}', - target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' - })).data; - core.info(`${name} is ${state}`); - - wokwi-test: - name: Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests - if: | - github.event.workflow_run.conclusion == 'success' || - github.event.workflow_run.conclusion == 'failure' || - github.event.workflow_run.conclusion == 'timed_out' - runs-on: ubuntu-latest - needs: get-artifacts - env: - id: ${{ needs.get-artifacts.outputs.ref }}-${{ github.event.workflow_run.head_sha || github.sha }}-${{ matrix.chip }}-${{ matrix.type }} - permissions: - actions: read - statuses: write - strategy: - fail-fast: false - matrix: - type: ${{ fromJson(needs.get-artifacts.outputs.types) }} - chip: ${{ fromJson(needs.get-artifacts.outputs.targets) }} - steps: - - name: Report pending - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - with: - script: | - const owner = '${{ github.repository_owner }}'; - const repo = '${{ github.repository }}'.split('/')[1]; - const sha = '${{ github.event.workflow_run.head_sha }}'; - core.debug(`owner: ${owner}`); - core.debug(`repo: ${repo}`); - core.debug(`sha: ${sha}`); - const { context: name, state } = (await github.rest.repos.createCommitStatus({ - context: 'Runtime Tests / Wokwi (${{ matrix.type }}, ${{ matrix.chip }}) / Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests (${{ github.event.workflow_run.event }} -> workflow_run)', - owner: owner, - repo: repo, - sha: sha, - state: 'pending', - target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' - })).data; - core.info(`${name} is ${state}`); - - - name: Check if already passed - id: get-cache-results - if: needs.get-artifacts.outputs.pr_num - uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - with: - key: tests-${{ env.id }}-results-wokwi - path: | - tests/**/*.xml - tests/**/result_*.json - - - name: Evaluate if tests should be run - id: check-tests - run: | - cache_exists=${{ steps.get-cache-results.outputs.cache-hit == 'true' }} - enabled=true - - if [[ $cache_exists == 'true' ]]; then - echo "Already ran, skipping" - enabled=false - fi - - echo "enabled=$enabled" >> $GITHUB_OUTPUT - - # Note that changes to the workflows and tests will only be picked up after the PR is merged - # DO NOT CHECKOUT THE USER'S REPOSITORY IN THIS WORKFLOW. IT HAS HIGH SECURITY RISKS. - - name: Checkout repository - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - ref: ${{ needs.get-artifacts.outputs.base || github.ref }} - - - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - with: - cache-dependency-path: tests/requirements.txt - cache: "pip" - python-version: "3.x" - - - name: Install dependencies - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - run: | - pip install -U pip - pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi - - - name: Install Wokwi CLI - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - run: curl -L https://wokwi.com/ci/install.sh | sh - - - name: Wokwi CI Server - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - uses: wokwi/wokwi-ci-server-action@a6fabb5a49e080158c7a1d121ea5b789536a82c3 # v1 - - - name: Get binaries - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - run-id: ${{ github.event.workflow_run.id }} - name: tests-bin-${{ matrix.chip }}-${{ matrix.type }} - path: | - ~/.arduino/tests/${{ matrix.chip }} - - - name: Run Tests - if: ${{ steps.check-tests.outputs.enabled == 'true' }} - env: - WOKWI_CLI_TOKEN: ${{ secrets.WOKWI_CLI_TOKEN }} - run: | - bash .github/scripts/tests_run.sh -c -type ${{ matrix.type }} -t ${{ matrix.chip }} -i 0 -m 1 -W ${{ env.WOKWI_TIMEOUT }} - - - name: Upload ${{ matrix.chip }} ${{ matrix.type }} Wokwi results as cache - uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 - if: steps.check-tests.outputs.enabled == 'true' && needs.get-artifacts.outputs.pr_num - with: - key: tests-${{ env.id }}-results-wokwi - path: | - tests/**/*.xml - tests/**/result_*.json - - - name: Upload ${{ matrix.chip }} ${{ matrix.type }} Wokwi results as artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: tests-results-wokwi-${{ matrix.chip }}-${{ matrix.type }} - overwrite: true - path: | - tests/**/*.xml - tests/**/result_*.json - - - name: Report conclusion - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - if: always() - with: - script: | - const owner = '${{ github.repository_owner }}'; - const repo = '${{ github.repository }}'.split('/')[1]; - const sha = '${{ github.event.workflow_run.head_sha }}'; - core.debug(`owner: ${owner}`); - core.debug(`repo: ${repo}`); - core.debug(`sha: ${sha}`); - const { context: name, state } = (await github.rest.repos.createCommitStatus({ - context: 'Runtime Tests / Wokwi (${{ matrix.type }}, ${{ matrix.chip }}) / Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests (${{ github.event.workflow_run.event }} -> workflow_run)', - owner: owner, - repo: repo, - sha: sha, - state: '${{ job.status }}', - target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' - })).data; - core.info(`${name} is ${state}`); diff --git a/.github/workflows/upload-idf-component.yml b/.github/workflows/upload-idf-component.yml index 687e721fbc2..8bb6956e6da 100644 --- a/.github/workflows/upload-idf-component.yml +++ b/.github/workflows/upload-idf-component.yml @@ -51,9 +51,9 @@ jobs: submodules: "recursive" - name: Upload components to the component registry - uses: espressif/upload-components-ci-action@b78a19fa5424714997596d3ecffa634aef8ae20b # v1.0.5 + uses: espressif/upload-components-ci-action@df9e740b912c009996df639ba7090c24e9a542c2 # v2.2.0 with: - name: arduino-esp32 + components: "arduino-esp32: ." # component_name: directory version: ${{ env.RELEASE_TAG }} namespace: espressif api_token: ${{ secrets.IDF_COMPONENT_API_TOKEN }} diff --git a/.gitignore b/.gitignore index d254d439834..66b8f1b8031 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,10 @@ libraries/Insights/examples/*/*.ino.zip # Ignore Lib Builder Docker run scripts /run.sh /run.ps1 + +# Ignore dependency analysis artifacts +/dependencies.json +/dependencies_reverse.json +/affected_sketches.txt +/affected_examples.txt +/ctags_* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 89a45022bc2..3d0ecd0cb34 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,25 +1,13 @@ workflow: rules: - # Disable those non-protected push triggered pipelines - - if: '$CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_BRANCH !~ /^release\/v/ && $CI_COMMIT_TAG !~ /^\d+\.\d+(\.\d+)?($|-)/ && $CI_PIPELINE_SOURCE == "push"' - when: never - # when running merged result pipelines, CI_COMMIT_SHA represents the temp commit it created. - # Please use PIPELINE_COMMIT_SHA at all places that require a commit sha of the original commit. - - if: $CI_OPEN_MERGE_REQUESTS != null - variables: - PIPELINE_COMMIT_SHA: $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA - IS_MR_PIPELINE: 1 - - if: $CI_OPEN_MERGE_REQUESTS == null - variables: - PIPELINE_COMMIT_SHA: $CI_COMMIT_SHA - IS_MR_PIPELINE: 0 - - if: '$CI_PIPELINE_SOURCE == "schedule"' - variables: - IS_SCHEDULED_RUN: "true" - - when: always + # Allow only when triggered manually (web), via API, or by a trigger token + - if: "$CI_PIPELINE_SOURCE =~ /^(trigger|api|web)$/" + when: always + # Deny all other sources + - when: never # Place the default settings in `.gitlab/workflows/common.yml` instead include: - ".gitlab/workflows/common.yml" - - ".gitlab/workflows/sample.yml" + - ".gitlab/workflows/hardware_tests_dynamic.yml" diff --git a/.gitlab/scripts/gen_hw_jobs.py b/.gitlab/scripts/gen_hw_jobs.py new file mode 100644 index 00000000000..5dd77cf56af --- /dev/null +++ b/.gitlab/scripts/gen_hw_jobs.py @@ -0,0 +1,525 @@ +#!/usr/bin/env python3 + +import argparse +import json +import yaml +import os +import sys +import copy +import traceback +from pathlib import Path +from typing import Iterable +from urllib.parse import urlencode +import urllib.request +import urllib.error + +# Resolve repository root from this script location +SCRIPT_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPT_DIR.parent.parent +TESTS_ROOT = REPO_ROOT / "tests" + +# Ensure we run from repo root so relative paths work consistently +try: + os.chdir(REPO_ROOT) +except Exception as e: + sys.stderr.write(f"[WARN] Failed to chdir to repo root '{REPO_ROOT}': {e}\n") + sys.stderr.write(traceback.format_exc() + "\n") + + +class PrettyDumper(yaml.SafeDumper): + def increase_indent(self, flow=False, indentless=False): + return super().increase_indent(flow, False) + + +def str_representer(dumper, data): + style = "|" if "\n" in data else None + return dumper.represent_scalar("tag:yaml.org,2002:str", data, style=style) + + +def read_yaml(p: Path): + try: + with p.open("r", encoding="utf-8") as f: + return yaml.safe_load(f) or {} + except Exception as e: + sys.stderr.write(f"[WARN] Failed to parse YAML file '{p}': {e}\n") + sys.stderr.write(traceback.format_exc() + "\n") + return {} + + +def find_tests() -> list[Path]: + tests = [] + if not TESTS_ROOT.exists(): + return tests + for ci in TESTS_ROOT.rglob("ci.yml"): + if ci.is_file(): + tests.append(ci) + return tests + + +def find_sketch_test_dirs(types_filter: list[str]) -> list[tuple[str, Path]]: + """ + Return list of (test_type, test_dir) where test_dir contains a sketch named /.ino + If types_filter provided, only include those types. + """ + results: list[tuple[str, Path]] = [] + if not TESTS_ROOT.exists(): + return results + for type_dir in TESTS_ROOT.iterdir(): + if not type_dir.is_dir(): + continue + test_type = type_dir.name + if types_filter and test_type not in types_filter: + continue + for candidate in type_dir.iterdir(): + if not candidate.is_dir(): + continue + sketch = candidate.name + ino = candidate / f"{sketch}.ino" + if ino.exists(): + results.append((test_type, candidate)) + return results + + +def load_tags_for_test(ci_json: dict, chip: str) -> set[str]: + tags = set() + # Global tags + v = ci_json.get("tags") + if isinstance(v, list): + for e in v: + if isinstance(e, str) and e.strip(): + tags.add(e.strip()) + # Per-SoC tags + soc_tags = ci_json.get("soc_tags") + if isinstance(soc_tags, dict): + v = soc_tags.get(chip) + if isinstance(v, list): + for e in v: + if isinstance(e, str) and e.strip(): + tags.add(e.strip()) + return tags + + +def test_enabled_for_target(ci_json: dict, chip: str) -> bool: + targets = ci_json.get("targets") + if isinstance(targets, dict): + v = targets.get(chip) + if v is False: + return False + return True + + +def platform_allowed(ci_json: dict, platform: str = "hardware") -> bool: + platforms = ci_json.get("platforms") + if isinstance(platforms, dict): + v = platforms.get(platform) + if v is False: + return False + return True + + +def sketch_name_from_ci(ci_path: Path) -> str: + # The sketch directory holds .ino named as the directory + sketch_dir = ci_path.parent + return sketch_dir.name + + +def sdkconfig_path_for(chip: str, sketch: str, ci_json: dict) -> Path: + # Match logic from tests_run.sh: if multiple FQBN entries -> build0.tmp + fqbn = ci_json.get("fqbn", {}) if isinstance(ci_json, dict) else {} + length = 0 + if isinstance(fqbn, dict): + v = fqbn.get(chip) + if isinstance(v, list): + length = len(v) + if length <= 1: + return Path.home() / f".arduino/tests/{chip}/{sketch}/build.tmp/sdkconfig" + return Path.home() / f".arduino/tests/{chip}/{sketch}/build0.tmp/sdkconfig" + + +def sdk_meets_requirements(sdkconfig: Path, ci_json: dict) -> bool: + # Mirror check_requirements in sketch_utils.sh + if not sdkconfig.exists(): + # Build might have been skipped or failed; allow parent to skip scheduling + return False + try: + requires = ci_json.get("requires") or [] + requires_any = ci_json.get("requires_any") or [] + content = sdkconfig.read_text(encoding="utf-8", errors="ignore") + # AND requirements + for req in requires: + if not isinstance(req, str): + continue + if not any(line.startswith(req) for line in content.splitlines()): + return False + # OR requirements + if requires_any: + ok = any( + any(line.startswith(req) for line in content.splitlines()) + for req in requires_any + if isinstance(req, str) + ) + if not ok: + return False + return True + except Exception as e: + sys.stderr.write(f"[WARN] Failed to evaluate requirements against '{sdkconfig}': {e}\n") + sys.stderr.write(traceback.format_exc() + "\n") + return False + + +def parse_list_arg(s: str) -> list[str]: + if not s: + return [] + txt = s.strip() + if txt.startswith("[") and txt.endswith("]"): + try: + return [str(x).strip() for x in json.loads(txt)] + except Exception as e: + sys.stderr.write(f"[WARN] Failed to parse JSON list '{txt}': {e}. Retrying with quote normalization.\n") + try: + fixed = txt.replace("'", '"') + return [str(x).strip() for x in json.loads(fixed)] + except Exception as e2: + sys.stderr.write( + f"[WARN] Failed to parse JSON list after normalization: {e2}. Falling back to CSV parsing.\n" + ) + # Fallback: comma-separated + return [part.strip() for part in txt.split(",") if part.strip()] + + +def _gitlab_auth_header() -> tuple[str, str]: + """Return header key and value for GitLab API auth, preferring PRIVATE-TOKEN, then JOB-TOKEN. + + Falls back to empty auth if neither is available. + """ + private = os.environ.get("GITLAB_API_TOKEN") or os.environ.get("PRIVATE_TOKEN") + if private: + return ("PRIVATE-TOKEN", private) + job = os.environ.get("CI_JOB_TOKEN") + if job: + return ("JOB-TOKEN", job) + return ("", "") + + +def _gitlab_api_get(path: str) -> tuple[int, dict | list | None]: + """Perform a GET to GitLab API v4 and return (status_code, json_obj_or_None). + + Uses project-level API base from CI env. Returns (0, None) if base env is missing. + """ + base = os.environ.get("CI_API_V4_URL") + if not base: + return 0, None + url = base.rstrip("/") + "/" + path.lstrip("/") + key, value = _gitlab_auth_header() + req = urllib.request.Request(url) + if key: + req.add_header(key, value) + try: + with urllib.request.urlopen(req, timeout=15) as resp: + status = resp.getcode() + data = resp.read() + try: + obj = json.loads(data.decode("utf-8")) if data else None + except Exception: + obj = None + return status, obj + except urllib.error.HTTPError as e: + try: + body = e.read().decode("utf-8") + except Exception: + body = str(e) + sys.stderr.write(f"[WARN] GitLab API GET {url} failed: {e} body={body}\n") + return e.code, None + except Exception as e: + sys.stderr.write(f"[WARN] GitLab API GET {url} error: {e}\n") + sys.stderr.write(traceback.format_exc() + "\n") + return -1, None + + +def list_project_runners() -> list[dict]: + """List runners available to this project via GitLab API. + + Requires CI vars CI_API_V4_URL and CI_PROJECT_ID and either GITLAB_API_TOKEN or CI_JOB_TOKEN. + Returns an empty list if not accessible. + """ + project_id = os.environ.get("CI_PROJECT_ID") + if not project_id: + return [] + + key, value = _gitlab_auth_header() + sys.stderr.write(f"[DEBUG] Attempting to list runners for project {project_id}\n") + sys.stderr.write(f"[DEBUG] Auth method: {'Authenticated' if key else 'None'}\n") + + runners: list[dict] = [] + page = 1 + per_page = 100 + while True: + q = urlencode({"per_page": per_page, "page": page}) + status, obj = _gitlab_api_get(f"projects/{project_id}/runners?{q}") + if status != 200 or not isinstance(obj, list): + # Project-scoped listing might be restricted for JOB-TOKEN in some instances. + # Return what we have (likely nothing) and let caller decide. + if status == 403: + sys.stderr.write("\n[ERROR] 403 Forbidden when listing project runners\n") + sys.stderr.write(f" Project ID: {project_id}\n") + sys.stderr.write(f" Authentication: {'Present' if key else 'None'}\n") + sys.stderr.write(" Endpoint: projects/{project_id}/runners\n\n") + + sys.stderr.write("Required permissions:\n") + sys.stderr.write(" - Token scope: 'api' (you likely have this)\n") + sys.stderr.write(" - Project role: Maintainer or Owner (you may be missing this)\n\n") + + sys.stderr.write("Solutions:\n") + sys.stderr.write(" 1. Ensure the token owner has Maintainer/Owner role on project {project_id}\n") + sys.stderr.write(" 2. Use a Group Access Token if available (has higher privileges)\n") + sys.stderr.write(" 3. Set environment variable: ASSUME_TAGGED_GROUPS_MISSING=0\n") + sys.stderr.write(" (This will skip runner enumeration and schedule all groups)\n\n") + break + runners.extend(x for x in obj if isinstance(x, dict)) + if len(obj) < per_page: + break + page += 1 + + # The /projects/:id/runners endpoint returns simplified objects without tag_list. + # Fetch full details for each runner to get tags. + sys.stderr.write(f"[DEBUG] Fetching full details for {len(runners)} runners to get tags...\n") + full_runners = [] + for runner in runners: + runner_id = runner.get("id") + if not runner_id: + continue + status, details = _gitlab_api_get(f"runners/{runner_id}") + if status == 200 and isinstance(details, dict): + full_runners.append(details) + else: + # If we can't get details, keep the basic info (no tags) + full_runners.append(runner) + + return full_runners + + +def runner_supports_tags(runner: dict, required_tags: Iterable[str]) -> bool: + tag_list = runner.get("tag_list") or [] + if not isinstance(tag_list, list): + return False + tags = {str(t).strip() for t in tag_list if isinstance(t, str) and t.strip()} + if not tags: + return False + # Skip paused/inactive runners + if runner.get("paused") is True: + return False + if runner.get("active") is False: + return False + return all(t in tags for t in required_tags) + + +def any_runner_matches(required_tags: Iterable[str], runners: list[dict]) -> bool: + req = [t for t in required_tags if t] + for r in runners: + try: + if runner_supports_tags(r, req): + return True + except Exception as e: + # Be robust to unexpected runner payloads + runner_id = r.get("id", "unknown") + sys.stderr.write(f"[WARN] Error checking runner #{runner_id} against required tags: {e}\n") + sys.stderr.write(traceback.format_exc() + "\n") + continue + return False + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--chips", required=True, help="Comma-separated or JSON array list of SoCs") + ap.add_argument( + "--types", + required=False, + default="validation", + help="Comma-separated or JSON array of test type directories under tests/", + ) + ap.add_argument("--out", required=True, help="Output YAML path for child pipeline") + ap.add_argument( + "--dry-run", action="store_true", help="Print planned groups/jobs and skip sdkconfig requirement checks" + ) + args = ap.parse_args() + + chips = parse_list_arg(args.chips) + types = parse_list_arg(args.types) + + print(f"Inputs: chips={chips or '[]'}, types={types or '[]'}") + print(f"Repo root: {REPO_ROOT}") + print(f"Tests root: {TESTS_ROOT}") + + # Aggregate mapping: (chip, frozenset(tags or generic), test_type) -> list of test paths + group_map: dict[tuple[str, frozenset[str], str], list[str]] = {} + all_ci = find_tests() + print(f"Discovered {len(all_ci)} ci.yml files under tests/") + + matched_count = 0 + for test_type, test_path in find_sketch_test_dirs(types): + ci_path = test_path / "ci.yml" + ci = read_yaml(ci_path) if ci_path.exists() else {} + test_dir = str(test_path) + sketch = test_path.name + for chip in chips: + tags = load_tags_for_test(ci, chip) + if not test_enabled_for_target(ci, chip): + continue + # Skip tests that explicitly disable the hardware platform + if not platform_allowed(ci, "hardware"): + continue + sdk = sdkconfig_path_for(chip, sketch, ci) + if not args.dry_run and not sdk_meets_requirements(sdk, ci): + continue + key_tags = tags.copy() + # SOC must always be one runner tag + key_tags.add(chip) + if len(key_tags) == 1: + # Only SOC present, add generic + key_tags.add("generic") + key = (chip, frozenset(sorted(key_tags)), test_type) + group_map.setdefault(key, []).append(test_dir) + matched_count += 1 + + print(f"Matched {matched_count} test entries into {len(group_map)} groups") + + # Load template job + template_path = REPO_ROOT / ".gitlab/workflows/hw_test_template.yml" + template = yaml.safe_load(template_path.read_text(encoding="utf-8")) + if not isinstance(template, dict) or "hw-test-template" not in template: + print("ERROR: hw_test_template.yml missing hw-test-template") + sys.exit(2) + base_job = template["hw-test-template"] + + # Build child pipeline YAML in deterministic order + jobs_entries = [] # list of (sort_key, job_name, job_dict) + + # Discover available runners + available_runners = list_project_runners() + if not available_runners: + print("\n[ERROR] Could not enumerate project runners!") + print("This is required to match test groups to runners by tags.") + print("\nPossible causes:") + print(" - No runners are registered to the project") + print(" - API token lacks required permissions (needs 'api' scope + Maintainer/Owner role)") + print(" - Network/API connectivity issues") + sys.exit(1) + + print(f"\n=== Available Runners ({len(available_runners)}) ===") + for runner in available_runners: + runner_id = runner.get("id", "?") + runner_desc = runner.get("description", "") + runner_tags = runner.get("tag_list", []) + runner_active = runner.get("active", False) + runner_paused = runner.get("paused", False) + status = "ACTIVE" if (runner_active and not runner_paused) else "INACTIVE/PAUSED" + print(f" Runner #{runner_id} ({status}): {runner_desc}") + print(f" Tags: {', '.join(runner_tags) if runner_tags else '(none)'}") + print("=" * 60 + "\n") + + # Track skipped groups for reporting + skipped_groups: list[dict] = [] + + print("\n=== Test Group Scheduling ===") + for (chip, tagset, test_type), test_dirs in group_map.items(): + tag_list = sorted(tagset) + # Build name suffix excluding the SOC itself to avoid duplication + non_soc_tags = [t for t in tag_list if t != chip] + tag_suffix = "-".join(non_soc_tags) if non_soc_tags else "generic" + + # Determine if any runner can serve this job + can_schedule = any_runner_matches(tag_list, available_runners) + print(f" Group: {chip}-{test_type}-{tag_suffix}") + print(f" Required tags: {', '.join(tag_list)}") + print(f" Tests: {len(test_dirs)}") + if can_schedule: + print(" ✓ Runner found - scheduling") + else: + print(" ✗ NO RUNNER FOUND - skipping") + + if can_schedule: + job_name = f"hw-{chip}-{test_type}-{tag_suffix}"[:255] + + # Clone base job and adjust (preserve key order using deepcopy) + job = copy.deepcopy(base_job) + # Ensure tags include SOC+extras + job["tags"] = tag_list + vars_block = job.get("variables", {}) + vars_block["TEST_CHIP"] = chip + vars_block["TEST_TYPE"] = test_type + # Provide list of test directories for this job + vars_block["TEST_LIST"] = "\n".join(sorted(test_dirs)) + job["variables"] = vars_block + + sort_key = (chip, test_type, tag_suffix) + jobs_entries.append((sort_key, job_name, job)) + else: + # Track skipped groups for reporting + skipped_groups.append( + { + "chip": chip, + "test_type": test_type, + "required_tags": tag_list, + "test_count": len(test_dirs), + } + ) + + # Print summary + print("\n=== Summary ===") + print(f" Scheduled groups: {len(jobs_entries)}") + print(f" Skipped groups (no runner): {len(skipped_groups)}") + if skipped_groups: + print("\n Skipped group details:") + for sg in skipped_groups: + chip = sg.get("chip") + test_type = sg.get("test_type") + tags = sg.get("required_tags", []) + test_count = sg.get("test_count", 0) + print(f" - {chip}-{test_type}: requires tags {tags}, {test_count} tests") + + # Order jobs by (chip, type, tag_suffix) + jobs = {} + for _, name, job in sorted(jobs_entries, key=lambda x: x[0]): + jobs[name] = job + + if args.dry_run: + print("Planned hardware test jobs:") + for name, job in jobs.items(): + tags = job.get("tags", []) + soc = job.get("variables", {}).get("TEST_CHIP") + ttype = job.get("variables", {}).get("TEST_TYPE") + tlist = job.get("variables", {}).get("TEST_LIST", "") + tests = [p for p in tlist.split("\n") if p] + print(f"- {name} tags={tags} soc={soc} type={ttype} tests={len(tests)}") + for t in tests: + print(f" * {t}") + + # If no jobs matched, create a no-op job to avoid failing trigger + if not jobs: + jobs["no-op"] = { + "stage": "test", + "script": ["echo No matching hardware tests to run"], + "rules": [{"when": "on_success"}], + } + + # Ensure child pipeline defines stages + child = {"stages": ["test"]} + + for name, job in jobs.items(): + child[name] = job + + if args.dry_run: + print("\n--- Generated child pipeline YAML (dry run) ---") + PrettyDumper.add_representer(str, str_representer) + sys.stdout.write(yaml.dump(child, Dumper=PrettyDumper, sort_keys=False, width=4096)) + return 0 + + out = Path(args.out) + + PrettyDumper.add_representer(str, str_representer) + out.write_text(yaml.dump(child, Dumper=PrettyDumper, sort_keys=False, width=4096), encoding="utf-8") + print(f"Wrote child pipeline with {len(jobs)} job(s) to {out}") + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.gitlab/scripts/get_artifacts.sh b/.gitlab/scripts/get_artifacts.sh new file mode 100644 index 00000000000..a29e76a9ba1 --- /dev/null +++ b/.gitlab/scripts/get_artifacts.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Disable shellcheck warning about $? uses. +# shellcheck disable=SC2181 + +set -e +set -o pipefail + +echo "Downloading test binaries for $TEST_CHIP from GitHub repository $GITHUB_REPOSITORY" +echo "Binaries run ID: $BINARIES_RUN_ID" +echo "Looking for artifact: test-bin-$TEST_CHIP-$TEST_TYPE" + +# Check if GitHub token is available +if [ -z "$GITHUB_DOWNLOAD_PAT" ]; then + echo "ERROR: GITHUB_DOWNLOAD_PAT not available in GitLab environment" + echo "Please set up GITHUB_DOWNLOAD_PAT in GitLab CI/CD variables" + exit 1 +fi + +# First, get the artifacts list and save it for debugging +echo "Fetching artifacts list from GitHub API..." +artifacts_response=$(curl -s -H "Authorization: token $GITHUB_DOWNLOAD_PAT" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/$GITHUB_REPOSITORY/actions/runs/$BINARIES_RUN_ID/artifacts") + +# Check if we got a valid response +if [ -z "$artifacts_response" ]; then + echo "ERROR: Empty response from GitHub API" + exit 1 +fi + +# Check for API errors +error_message=$(echo "$artifacts_response" | jq -r '.message // empty' 2>/dev/null) +if [ -n "$error_message" ]; then + echo "ERROR: GitHub API returned error: $error_message" + exit 1 +fi + +# List all available artifacts for debugging +echo "Available artifacts:" +echo "$artifacts_response" | jq -r '.artifacts[]?.name // "No artifacts found"' 2>/dev/null || echo "Could not parse artifacts" + +# Find the download URL for our specific artifact +download_url=$(echo "$artifacts_response" | jq -r ".artifacts[] | select(.name==\"test-bin-$TEST_CHIP-$TEST_TYPE\") | .archive_download_url" 2>/dev/null) + +if [ "$download_url" = "null" ] || [ -z "$download_url" ]; then + echo "ERROR: Could not find artifact 'test-bin-$TEST_CHIP-$TEST_TYPE'" + echo "This could mean:" + echo "1. The artifact name doesn't match exactly" + echo "2. The artifacts haven't been uploaded yet" + echo "3. The GitHub run ID is incorrect" + exit 1 +fi + +echo "Found download URL: $download_url" + +# Download the artifact +echo "Downloading artifact..." +curl -H "Authorization: token $GITHUB_DOWNLOAD_PAT" -L "$download_url" -o test-binaries.zip + +if [ $? -ne 0 ] || [ ! -f test-binaries.zip ]; then + echo "ERROR: Failed to download artifact" + exit 1 +fi + +echo "Extracting binaries..." +unzip -q -o test-binaries.zip -d ~/.arduino/tests/"$TEST_CHIP"/ + +if [ $? -ne 0 ]; then + echo "ERROR: Failed to extract binaries" + exit 1 +fi + +rm -f test-binaries.zip +echo "Successfully downloaded and extracted test binaries" diff --git a/.gitlab/scripts/get_results.sh b/.gitlab/scripts/get_results.sh new file mode 100644 index 00000000000..cdfe2e64f96 --- /dev/null +++ b/.gitlab/scripts/get_results.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +set -euo pipefail + +echo "Collecting artifacts from child pipeline(s)" + +api="$CI_API_V4_URL" +proj="$CI_PROJECT_ID" +parent="$CI_PIPELINE_ID" + +# Choose auth header (prefer PRIVATE-TOKEN if provided) +AUTH_HEADER="JOB-TOKEN: $CI_JOB_TOKEN" +if [ -n "${GITLAB_API_TOKEN:-}" ]; then + AUTH_HEADER="PRIVATE-TOKEN: $GITLAB_API_TOKEN" +fi + +# Verify project is reachable +if ! curl -sf --header "$AUTH_HEADER" "$api/projects/$proj" >/dev/null; then + echo "WARNING: Unable to access project $proj via API (token scope?)" + exit 1 +fi + +bridges=$(curl -s --header "$AUTH_HEADER" "$api/projects/$proj/pipelines/$parent/bridges") +# Ensure we got a JSON array +if ! echo "$bridges" | jq -e 'type=="array"' >/dev/null 2>&1; then + echo "WARNING: Unexpected bridges response:"; echo "$bridges" + exit 1 +fi + +child_ids=$(echo "$bridges" | jq -r '.[] | select(.name=="trigger-hw-tests") | .downstream_pipeline.id') +mkdir -p aggregated + +for cid in $child_ids; do + echo "Child pipeline: $cid" + + jobs=$(curl -s --header "$AUTH_HEADER" "$api/projects/$proj/pipelines/$cid/jobs?per_page=100") + if ! echo "$jobs" | jq -e 'type=="array"' >/dev/null 2>&1; then + echo "WARNING: Unable to list jobs for child $cid"; echo "$jobs" + exit 1 + fi + + ids=$(echo "$jobs" | jq -r '.[] | select(.artifacts_file!=null) | .id') + failed=false + for jid in $ids; do + echo "Downloading artifacts from job $jid" + curl --header "$AUTH_HEADER" -L -s "$api/projects/$proj/jobs/$jid/artifacts" -o artifact.zip || true + if [ -f artifact.zip ]; then + unzip -q -o artifact.zip -d . >/dev/null 2>&1 || true + else + echo "Job $jid has no artifacts" + failed=true + fi + rm -f artifact.zip + done +done + +if $failed; then + echo "Some jobs failed to download artifacts" + exit 1 +fi diff --git a/.gitlab/scripts/install_dependencies.sh b/.gitlab/scripts/install_dependencies.sh new file mode 100644 index 00000000000..ac7c6ff0b0a --- /dev/null +++ b/.gitlab/scripts/install_dependencies.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash + +set -euo pipefail + +export DEBIAN_FRONTEND=noninteractive + +echo "[deps] Updating apt indexes" +apt-get update -y + +echo "[deps] Installing base packages" +apt-get install -y jq unzip curl wget + +echo "[deps] Installing Python packages" +pip3 install PyYAML + +echo "[deps] Installing yq (mikefarah/yq) for current architecture" +YQ_VERSION="v4.48.1" +ARCH="$(uname -m)" +case "$ARCH" in + x86_64) YQ_BINARY=yq_linux_amd64 ;; + aarch64|arm64) YQ_BINARY=yq_linux_arm64 ;; + armv7l|armv6l|armhf|arm) YQ_BINARY=yq_linux_arm ;; + i386|i686) YQ_BINARY=yq_linux_386 ;; + *) echo "Unsupported architecture: $ARCH"; exit 1 ;; +esac + +echo "[deps] Downloading mikefarah/yq $YQ_VERSION ($YQ_BINARY)" +wget -q https://github.com/mikefarah/yq/releases/download/${YQ_VERSION}/${YQ_BINARY} -O /usr/bin/yq +chmod +x /usr/bin/yq +yq --version + +echo "[deps] Dependencies installed successfully" diff --git a/.gitlab/workflows/common.yml b/.gitlab/workflows/common.yml index 611e1d974f4..0f4bda9bcce 100644 --- a/.gitlab/workflows/common.yml +++ b/.gitlab/workflows/common.yml @@ -4,13 +4,16 @@ stages: - pre_check + - generate - build - test + - trigger + - collect - result variables: ESP_IDF_VERSION: "5.5" - ESP_ARDUINO_VERSION: "3.3.0" + ESP_ARDUINO_VERSION: "3.3.4" ############# # `default` # diff --git a/.gitlab/workflows/hardware_tests_dynamic.yml b/.gitlab/workflows/hardware_tests_dynamic.yml new file mode 100644 index 00000000000..9b88fabab34 --- /dev/null +++ b/.gitlab/workflows/hardware_tests_dynamic.yml @@ -0,0 +1,77 @@ +############################### +# Dynamic Hardware Tests Parent +############################### + +# This parent workflow generates a dynamic child pipeline with jobs grouped +# by SOC + runner tags derived from tests' ci.yml, then triggers it and waits. + +generate-hw-tests: + stage: generate + image: python:3.12-bookworm + rules: + - if: $CI_PIPELINE_SOURCE == "trigger" + when: on_success + variables: + DEBIAN_FRONTEND: "noninteractive" + TEST_TYPES: $TEST_TYPES + TEST_CHIPS: $TEST_CHIPS + before_script: + - bash .gitlab/scripts/install_dependencies.sh + script: + - mkdir -p ~/.arduino/tests + - | + # Download artifacts for all requested chips/types so sdkconfig exists for grouping + CHIPS=$(echo "$TEST_CHIPS" | tr -d "[]' " | tr ',' ' ') + TYPES=$(echo "$TEST_TYPES" | tr -d "[]' " | tr ',' ' ') + for chip in $CHIPS; do + for t in $TYPES; do + export TEST_CHIP="$chip" + export TEST_TYPE="$t" + echo "Fetching artifacts for chip=$chip type=$t" + bash .gitlab/scripts/get_artifacts.sh + done + done + - python3 .gitlab/scripts/gen_hw_jobs.py --chips "$TEST_CHIPS" --types "$TEST_TYPES" --out child-hw-jobs.yml + artifacts: + when: always + expire_in: 7 days + paths: + - child-hw-jobs.yml + +trigger-hw-tests: + stage: trigger + needs: ["generate-hw-tests"] + rules: + - if: $CI_PIPELINE_SOURCE == "trigger" + when: on_success + variables: + # Forward common context to children + BINARIES_RUN_ID: $BINARIES_RUN_ID + GITHUB_REPOSITORY: $GITHUB_REPOSITORY + PIPELINE_ID: $PIPELINE_ID + trigger: + include: + - artifact: child-hw-jobs.yml + job: generate-hw-tests + strategy: depend + +collect-hw-results: + stage: result + image: python:3.12-bookworm + needs: ["trigger-hw-tests"] + rules: + - if: $CI_PIPELINE_SOURCE == "trigger" + when: always + before_script: + - apt-get update && apt-get install -y jq curl unzip + script: + - bash .gitlab/scripts/get_results.sh + artifacts: + name: "hw-test-results-aggregated" + expire_in: 7 days + when: always + paths: + - "tests/**/*.xml" + - "tests/**/result_*.json" + reports: + junit: "tests/**/*.xml" diff --git a/.gitlab/workflows/hw_test_template.yml b/.gitlab/workflows/hw_test_template.yml new file mode 100644 index 00000000000..f0fddebcd0e --- /dev/null +++ b/.gitlab/workflows/hw_test_template.yml @@ -0,0 +1,64 @@ +######################## +# HW Test Job Template # +######################## + +# This template is used to generate the pipeline for each hardware test. +# It is triggered in hardware_tests_dynamic.yml after being generated by gen_hw_jobs.py. + +include: + - local: ".gitlab/workflows/common.yml" + +# Single job template to be cloned by the dynamic generator +hw-test-template: + stage: test + image: python:3.12-bookworm + timeout: 5h + + rules: + - when: on_success + + variables: + RUNNER_SCRIPT_TIMEOUT: 4h + DEBIAN_FRONTEND: "noninteractive" + TEST_TYPE: $TEST_TYPE + TEST_CHIP: $TEST_CHIP + PIPELINE_ID: $PIPELINE_ID + BINARIES_RUN_ID: $BINARIES_RUN_ID + GITHUB_REPOSITORY: $GITHUB_REPOSITORY + + tags: + - $TEST_CHIP + + before_script: + - echo "Running hardware tests for chip:$TEST_CHIP type:$TEST_TYPE" + - echo "Pipeline ID:$PIPELINE_ID" + - echo "Running hardware tests for chip:$TEST_CHIP" + - bash .gitlab/scripts/install_dependencies.sh + - rm -rf ~/.arduino/tests + - mkdir -p ~/.arduino/tests/$TEST_CHIP + - echo Fetching binaries for $TEST_CHIP $TEST_TYPE + - bash .gitlab/scripts/get_artifacts.sh + - pip install -r tests/requirements.txt --extra-index-url https://dl.espressif.com/pypi + + script: + - echo "Using binaries for $TEST_CHIP" + - ls -laR ~/.arduino/tests || true + - | + set -e + rc=0 + while IFS= read -r d; do + [ -z "$d" ] && continue; + sketch=$(basename "$d"); + echo Running $sketch in $d; + bash .github/scripts/tests_run.sh -t "$TEST_CHIP" -s "$sketch" -e -wifi-ssid "$RUNNER_WIFI_SSID" -wifi-password "$RUNNER_WIFI_PASSWORD" || rc=$?; + done <<< "$TEST_LIST"; exit $rc + + artifacts: + name: "hw-test-results-$TEST_CHIP-$TEST_TYPE" + expire_in: 7 days + when: always + paths: + - "tests/**/*.xml" + - "tests/**/result_*.json" + reports: + junit: "tests/**/*.xml" diff --git a/.gitlab/workflows/sample.yml b/.gitlab/workflows/sample.yml deleted file mode 100644 index 32b6fce042d..00000000000 --- a/.gitlab/workflows/sample.yml +++ /dev/null @@ -1,6 +0,0 @@ -hello-world: - stage: test - script: - - echo "Hello, World from GitLab CI!" - rules: - - if: $CI_PIPELINE_SOURCE == "push" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d425c46eae..7709c257bb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -108,3 +108,20 @@ repos: - id: vale language_version: "1.23.2" types_or: [markdown, rst] + + # Always leave this last to ensure that all hooks have already run + - repo: local + hooks: + - id: git-diff + name: git diff + entry: | + bash -c ' + if [ "$CI" = "true" ]; then + if git diff --exit-code; then + echo "pending_commit=0" >> $GITHUB_OUTPUT + else + echo "pending_commit=1" >> $GITHUB_OUTPUT + fi + fi + ' + language: system diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c9800dfe2b..a0455def056 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(CORE_SRCS cores/esp32/esp32-hal-cpu.c cores/esp32/esp32-hal-dac.c cores/esp32/esp32-hal-gpio.c + cores/esp32/esp32-hal-hosted.c cores/esp32/esp32-hal-i2c.c cores/esp32/esp32-hal-i2c-ng.c cores/esp32/esp32-hal-i2c-slave.c @@ -54,6 +55,7 @@ set(CORE_SRCS cores/esp32/freertos_stats.cpp cores/esp32/FunctionalInterrupt.cpp cores/esp32/HardwareSerial.cpp + cores/esp32/HashBuilder.cpp cores/esp32/HEXBuilder.cpp cores/esp32/IPAddress.cpp cores/esp32/libb64/cdecode.c @@ -62,7 +64,6 @@ set(CORE_SRCS cores/esp32/main.cpp cores/esp32/MD5Builder.cpp cores/esp32/Print.cpp - cores/esp32/SHA1Builder.cpp cores/esp32/stdlib_noniso.c cores/esp32/Stream.cpp cores/esp32/StreamString.cpp @@ -89,10 +90,12 @@ set(ARDUINO_ALL_LIBRARIES ESP_I2S ESP_NOW ESP_SR + ESP_HostedOTA ESPmDNS Ethernet FFat FS + Hash HTTPClient HTTPUpdate Insights @@ -144,6 +147,9 @@ set(ARDUINO_LIBRARY_ESP_SR_SRCS libraries/ESP_SR/src/ESP_SR.cpp libraries/ESP_SR/src/esp32-hal-sr.c) +set(ARDUINO_LIBRARY_ESP_HostedOTA_SRCS + libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp) + set(ARDUINO_LIBRARY_ESPmDNS_SRCS libraries/ESPmDNS/src/ESPmDNS.cpp) set(ARDUINO_LIBRARY_Ethernet_SRCS libraries/Ethernet/src/ETH.cpp) @@ -154,6 +160,13 @@ set(ARDUINO_LIBRARY_FS_SRCS libraries/FS/src/FS.cpp libraries/FS/src/vfs_api.cpp) +set(ARDUINO_LIBRARY_Hash_SRCS + libraries/Hash/src/SHA1Builder.cpp + libraries/Hash/src/SHA2Builder.cpp + libraries/Hash/src/SHA3Builder.cpp + libraries/Hash/src/PBKDF2_HMACBuilder.cpp + ) + set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp) set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp) @@ -178,11 +191,16 @@ set(ARDUINO_LIBRARY_Matter_SRCS libraries/Matter/src/MatterEndpoints/MatterEnhancedColorLight.cpp libraries/Matter/src/MatterEndpoints/MatterFan.cpp libraries/Matter/src/MatterEndpoints/MatterTemperatureSensor.cpp + libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp libraries/Matter/src/MatterEndpoints/MatterHumiditySensor.cpp libraries/Matter/src/MatterEndpoints/MatterContactSensor.cpp + libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp + libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp + libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp libraries/Matter/src/MatterEndpoints/MatterPressureSensor.cpp libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp libraries/Matter/src/MatterEndpoints/MatterOnOffPlugin.cpp + libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp libraries/Matter/src/Matter.cpp libraries/Matter/src/MatterEndPoint.cpp) @@ -307,6 +325,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeeBinary.cpp libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp libraries/Zigbee/src/ep/ZigbeeFanControl.cpp + libraries/Zigbee/src/ep/ZigbeeMultistate.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS @@ -373,7 +392,7 @@ if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThre endif() endif() -if(IDF_TARGET STREQUAL "esp32p4") +if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3" OR IDF_TARGET STREQUAL "esp32p4") list(APPEND requires esp_driver_touch_sens) endif() diff --git a/Kconfig.projbuild b/Kconfig.projbuild index 9966463f8c1..a2761749e13 100644 --- a/Kconfig.projbuild +++ b/Kconfig.projbuild @@ -147,6 +147,12 @@ config ARDUINO_UDP_TASK_PRIORITY help Select at what priority you want the UDP task to run. +config ARDUINO_UDP_TASK_STACK_SIZE + int "UDP task stack size" + default 4096 + help + Amount of stack available for the UDP task. + config ARDUINO_ISR_IRAM bool "Run interrupts in IRAM" default "n" @@ -341,9 +347,15 @@ config ARDUINO_SELECTIVE_PPP depends on ARDUINO_SELECTIVE_COMPILATION default y +config ARDUINO_SELECTIVE_Hash + bool "Enable Hash" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + config ARDUINO_SELECTIVE_ArduinoOTA bool "Enable ArduinoOTA" depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network + select ARDUINO_SELECTIVE_Hash select ARDUINO_SELECTIVE_ESPmDNS default y @@ -383,6 +395,7 @@ config ARDUINO_SELECTIVE_WebServer depends on ARDUINO_SELECTIVE_COMPILATION && ARDUINO_SELECTIVE_Network default y select ARDUINO_SELECTIVE_FS + select ARDUINO_SELECTIVE_Hash config ARDUINO_SELECTIVE_WiFi bool "Enable WiFi" diff --git a/README.md b/README.md index f40315c03cc..e6a0eb2dffb 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# Arduino core for the ESP32, ESP32-C3, ESP32-C6, ESP32-H2, ESP32-P4, ESP32-S2 and ESP32-S3. +# Arduino core for the ESP32, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, ESP32-P4, ESP32-S2 and ESP32-S3. [![Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=push&label=Compilation%20Tests)](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Apush) [![Verbose Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=schedule&label=Compilation%20Tests%20(Verbose))](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Aschedule) [![External Libraries Test](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/lib.yml?branch=master&event=schedule&label=External%20Libraries%20Test)](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md) -[![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/badge.svg)](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/RUNTIME_TESTS_REPORT.md) +[![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-test-results/badge.svg)](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-test-results/RUNTIME_TEST_RESULTS.md) ### Need help or have a question? Join the chat at [Discord](https://discord.gg/8xY6e9crwv) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) @@ -68,6 +68,7 @@ Here are the ESP32 series supported by the Arduino-ESP32 project: |----------|:----------:|:---------------:|:-------------------------------------------------------------------------------------------------:| | ESP32 | Yes | Yes | [ESP32](https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf) | | ESP32-C3 | Yes | Yes | [ESP32-C3](https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf) | +| ESP32-C5 | Yes | Yes | [ESP32-C5](https://www.espressif.com/sites/default/files/documentation/esp32-c5_datasheet_en.pdf) | | ESP32-C6 | Yes | Yes | [ESP32-C6](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) | | ESP32-H2 | Yes | Yes | [ESP32-H2](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) | | ESP32-P4 | Yes | Yes | [ESP32-P4](https://www.espressif.com/sites/default/files/documentation/esp32-p4_datasheet_en.pdf) | @@ -75,7 +76,7 @@ Here are the ESP32 series supported by the Arduino-ESP32 project: | ESP32-S3 | Yes | Yes | [ESP32-S3](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf) | > [!NOTE] -> ESP32-C2 is also supported by Arduino-ESP32 but requires using Arduino as an ESP-IDF component or rebuilding the static libraries. +> ESP32-C2 and ESP32-C61 are also supported by Arduino-ESP32 but require using Arduino as an ESP-IDF component or rebuilding the static libraries. > For more information, see the [Arduino as an ESP-IDF component documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/esp-idf_component.html) or the > [Lib Builder documentation](https://docs.espressif.com/projects/arduino-esp32/en/latest/lib_builder.html), respectively. diff --git a/boards.txt b/boards.txt index 52554372ef4..f951a0255fe 100644 --- a/boards.txt +++ b/boards.txt @@ -19,6 +19,7 @@ menu.EraseFlash=Erase All Flash Before Sketch Upload menu.JTAGAdapter=JTAG Adapter menu.ZigbeeMode=Zigbee Mode menu.PinNumbers=Pin Numbering +menu.ChipVariant=Chip Variant # Custom options menu.Revision=Board Revision @@ -29,6 +30,8 @@ menu.LORAWAN_PREAMBLE_LENGTH=LoRaWan Preamble Length menu.SLOW_CLK_TPYE=Slow Clk Type(only for LoRaWAN) menu.einksize=E-Ink Display Size menu.NetworkLogLevel=Network Log Level +menu.DisplayModel=Display Model + ############################################################## ### DO NOT PUT BOARDS ABOVE THE OFFICIAL ESPRESSIF BOARDS! ### ############################################################## @@ -105,7 +108,7 @@ esp32c2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c2.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -247,7 +250,7 @@ esp32c5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c5.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c5.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -396,6 +399,7 @@ esp32p4.build.target=esp esp32p4.build.mcu=esp32p4 esp32p4.build.core=esp32 esp32p4.build.variant=esp32p4 +esp32p4.build.chip_variant=esp32p4_es esp32p4.build.board=ESP32P4_DEV esp32p4.build.bootloader_addr=0x2000 @@ -412,6 +416,13 @@ esp32p4.build.boot=qio esp32p4.build.partitions=default esp32p4.build.defines= +esp32p4.menu.ChipVariant.prev3=Before v3.00 +esp32p4.menu.ChipVariant.prev3.build.chip_variant=esp32p4_es +esp32p4.menu.ChipVariant.prev3.build.f_cpu=360000000L +esp32p4.menu.ChipVariant.postv3=v3.00 or newer +esp32p4.menu.ChipVariant.postv3.build.chip_variant=esp32p4 +esp32p4.menu.ChipVariant.postv3.build.f_cpu=400000000L + ## IDE 2.0 Seems to not update the value esp32p4.menu.JTAGAdapter.default=Disabled esp32p4.menu.JTAGAdapter.default.build.copy_jtag_files=0 @@ -484,7 +495,7 @@ esp32p4.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32p4.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32p4.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32p4.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32p4.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32p4.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32p4.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32p4.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32p4.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -502,16 +513,14 @@ esp32p4.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 esp32p4.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) esp32p4.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB esp32p4.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 +esp32p4.menu.PartitionScheme.esp_sr_16=ESP SR 16M (3MB APP/7MB SPIFFS/2.9MB MODEL) +esp32p4.menu.PartitionScheme.esp_sr_16.upload.maximum_size=3145728 +esp32p4.menu.PartitionScheme.esp_sr_16.upload.extra_flags=0xD10000 {build.path}/srmodels.bin +esp32p4.menu.PartitionScheme.esp_sr_16.build.partitions=esp_sr_16 esp32p4.menu.PartitionScheme.custom=Custom esp32p4.menu.PartitionScheme.custom.build.partitions= esp32p4.menu.PartitionScheme.custom.upload.maximum_size=16777216 -## From https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/kconfig.html#config-esp-default-cpu-freq-mhz -esp32p4.menu.CPUFreq.360=360MHz -esp32p4.menu.CPUFreq.360.build.f_cpu=360000000L -esp32p4.menu.CPUFreq.40=40MHz -esp32p4.menu.CPUFreq.40.build.f_cpu=40000000L - esp32p4.menu.FlashMode.qio=QIO esp32p4.menu.FlashMode.qio.build.flash_mode=dio esp32p4.menu.FlashMode.qio.build.boot=qio @@ -654,7 +663,7 @@ esp32h2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32h2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32h2.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32h2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32h2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32h2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32h2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32h2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32h2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -852,7 +861,7 @@ esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c6.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -977,6 +986,176 @@ esp32c6.menu.ZigbeeMode.zczr_debug.build.zigbee_libs=-lesp_zb_api.zczr.debug -lz ############################################################## +esp32c61.name=ESP32C61 Dev Module +esp32c61.hide=true + +esp32c61.bootloader.tool=esptool_py +esp32c61.bootloader.tool.default=esptool_py + +esp32c61.upload.tool=esptool_py +esp32c61.upload.tool.default=esptool_py +esp32c61.upload.tool.network=esp_ota + +esp32c61.upload.maximum_size=1310720 +esp32c61.upload.maximum_data_size=327680 +esp32c61.upload.flags= +esp32c61.upload.extra_flags= +esp32c61.upload.use_1200bps_touch=false +esp32c61.upload.wait_for_upload_port=false + +esp32c61.serial.disableDTR=false +esp32c61.serial.disableRTS=false + +esp32c61.build.tarch=riscv32 +esp32c61.build.target=esp +esp32c61.build.mcu=esp32c61 +esp32c61.build.core=esp32 +esp32c61.build.variant=esp32c61 +esp32c61.build.board=ESP32C61_DEV +esp32c61.build.bootloader_addr=0x0 + +esp32c61.build.cdc_on_boot=0 +esp32c61.build.f_cpu=160000000L +esp32c61.build.flash_size=4MB +esp32c61.build.flash_freq=80m +esp32c61.build.flash_mode=qio +esp32c61.build.boot=qio +esp32c61.build.partitions=default +esp32c61.build.defines= + +## IDE 2.0 Seems to not update the value +esp32c61.menu.JTAGAdapter.default=Disabled +esp32c61.menu.JTAGAdapter.default.build.copy_jtag_files=0 +esp32c61.menu.JTAGAdapter.builtin=Integrated USB JTAG +esp32c61.menu.JTAGAdapter.builtin.build.openocdscript=esp32c61-builtin.cfg +esp32c61.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +esp32c61.menu.JTAGAdapter.external=FTDI Adapter +esp32c61.menu.JTAGAdapter.external.build.openocdscript=esp32c61-ftdi.cfg +esp32c61.menu.JTAGAdapter.external.build.copy_jtag_files=1 +esp32c61.menu.JTAGAdapter.bridge=ESP USB Bridge +esp32c61.menu.JTAGAdapter.bridge.build.openocdscript=esp32c61-bridge.cfg +esp32c61.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +esp32c61.menu.PSRAM.disabled=Disabled +esp32c61.menu.PSRAM.disabled.build.defines= +esp32c61.menu.PSRAM.enabled=Enabled +esp32c61.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM + +esp32c61.menu.CDCOnBoot.default=Disabled +esp32c61.menu.CDCOnBoot.default.build.cdc_on_boot=0 +esp32c61.menu.CDCOnBoot.cdc=Enabled +esp32c61.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +esp32c61.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +esp32c61.menu.PartitionScheme.default.build.partitions=default +esp32c61.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +esp32c61.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +esp32c61.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +esp32c61.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +esp32c61.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +esp32c61.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +esp32c61.menu.PartitionScheme.minimal.build.partitions=minimal +esp32c61.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +esp32c61.menu.PartitionScheme.no_fs.build.partitions=no_fs +esp32c61.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +esp32c61.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +esp32c61.menu.PartitionScheme.no_ota.build.partitions=no_ota +esp32c61.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +esp32c61.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +esp32c61.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +esp32c61.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +esp32c61.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +esp32c61.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +esp32c61.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +esp32c61.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +esp32c61.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +esp32c61.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +esp32c61.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +esp32c61.menu.PartitionScheme.huge_app.build.partitions=huge_app +esp32c61.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +esp32c61.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c61.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +esp32c61.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +esp32c61.menu.PartitionScheme.rainmaker=RainMaker 4MB +esp32c61.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +esp32c61.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +esp32c61.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +esp32c61.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +esp32c61.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +esp32c61.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB +esp32c61.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB +esp32c61.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 +esp32c61.menu.PartitionScheme.custom=Custom +esp32c61.menu.PartitionScheme.custom.build.partitions= +esp32c61.menu.PartitionScheme.custom.upload.maximum_size=8388608 + +esp32c61.menu.CPUFreq.160=160MHz (WiFi) +esp32c61.menu.CPUFreq.160.build.f_cpu=160000000L +esp32c61.menu.CPUFreq.120=120MHz (WiFi) +esp32c61.menu.CPUFreq.120.build.f_cpu=120000000L +esp32c61.menu.CPUFreq.80=80MHz (WiFi) +esp32c61.menu.CPUFreq.80.build.f_cpu=80000000L +esp32c61.menu.CPUFreq.40=40MHz +esp32c61.menu.CPUFreq.40.build.f_cpu=40000000L +esp32c61.menu.CPUFreq.20=20MHz +esp32c61.menu.CPUFreq.20.build.f_cpu=20000000L +esp32c61.menu.CPUFreq.10=10MHz +esp32c61.menu.CPUFreq.10.build.f_cpu=10000000L + +esp32c61.menu.FlashMode.qio=QIO +esp32c61.menu.FlashMode.qio.build.flash_mode=dio +esp32c61.menu.FlashMode.qio.build.boot=qio +esp32c61.menu.FlashMode.dio=DIO +esp32c61.menu.FlashMode.dio.build.flash_mode=dio +esp32c61.menu.FlashMode.dio.build.boot=dio + +esp32c61.menu.FlashFreq.80=80MHz +esp32c61.menu.FlashFreq.80.build.flash_freq=80m +esp32c61.menu.FlashFreq.40=40MHz +esp32c61.menu.FlashFreq.40.build.flash_freq=40m + +esp32c61.menu.FlashSize.4M=4MB (32Mb) +esp32c61.menu.FlashSize.4M.build.flash_size=4MB +esp32c61.menu.FlashSize.8M=8MB (64Mb) +esp32c61.menu.FlashSize.8M.build.flash_size=8MB +esp32c61.menu.FlashSize.2M=2MB (16Mb) +esp32c61.menu.FlashSize.2M.build.flash_size=2MB + +esp32c61.menu.UploadSpeed.921600=921600 +esp32c61.menu.UploadSpeed.921600.upload.speed=921600 +esp32c61.menu.UploadSpeed.115200=115200 +esp32c61.menu.UploadSpeed.115200.upload.speed=115200 +esp32c61.menu.UploadSpeed.256000.windows=256000 +esp32c61.menu.UploadSpeed.256000.upload.speed=256000 +esp32c61.menu.UploadSpeed.230400.windows.upload.speed=256000 +esp32c61.menu.UploadSpeed.230400=230400 +esp32c61.menu.UploadSpeed.230400.upload.speed=230400 +esp32c61.menu.UploadSpeed.460800.linux=460800 +esp32c61.menu.UploadSpeed.460800.macosx=460800 +esp32c61.menu.UploadSpeed.460800.upload.speed=460800 +esp32c61.menu.UploadSpeed.512000.windows=512000 +esp32c61.menu.UploadSpeed.512000.upload.speed=512000 + +esp32c61.menu.DebugLevel.none=None +esp32c61.menu.DebugLevel.none.build.code_debug=0 +esp32c61.menu.DebugLevel.error=Error +esp32c61.menu.DebugLevel.error.build.code_debug=1 +esp32c61.menu.DebugLevel.warn=Warn +esp32c61.menu.DebugLevel.warn.build.code_debug=2 +esp32c61.menu.DebugLevel.info=Info +esp32c61.menu.DebugLevel.info.build.code_debug=3 +esp32c61.menu.DebugLevel.debug=Debug +esp32c61.menu.DebugLevel.debug.build.code_debug=4 +esp32c61.menu.DebugLevel.verbose=Verbose +esp32c61.menu.DebugLevel.verbose.build.code_debug=5 + +esp32c61.menu.EraseFlash.none=Disabled +esp32c61.menu.EraseFlash.none.upload.erase_cmd= +esp32c61.menu.EraseFlash.all=Enabled +esp32c61.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + esp32s3.name=ESP32S3 Dev Module esp32s3.bootloader.tool=esptool_py @@ -1138,7 +1317,7 @@ esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -1315,7 +1494,7 @@ esp32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -1523,7 +1702,7 @@ esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -1706,7 +1885,7 @@ esp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -1886,7 +2065,7 @@ esp32da.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32da.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32da.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32da.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32da.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32da.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32da.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32da.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32da.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -2027,9 +2206,6 @@ esp32wrover.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5M esp32wrover.menu.PartitionScheme.default.build.partitions=default esp32wrover.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) esp32wrover.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -esp32wrover.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -esp32wrover.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -esp32wrover.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 esp32wrover.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) esp32wrover.menu.PartitionScheme.minimal.build.partitions=minimal esp32wrover.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -2047,12 +2223,9 @@ esp32wrover.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32wrover.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32wrover.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32wrover.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32wrover.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32wrover.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32wrover.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32wrover.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -esp32wrover.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -esp32wrover.menu.PartitionScheme.fatflash.build.partitions=ffat -esp32wrover.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 esp32wrover.menu.PartitionScheme.rainmaker=RainMaker 4MB esp32wrover.menu.PartitionScheme.rainmaker.build.partitions=rainmaker esp32wrover.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -2061,7 +2234,7 @@ esp32wrover.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no esp32wrover.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 esp32wrover.menu.PartitionScheme.custom=Custom esp32wrover.menu.PartitionScheme.custom.build.partitions= -esp32wrover.menu.PartitionScheme.custom.upload.maximum_size=16777216 +esp32wrover.menu.PartitionScheme.custom.upload.maximum_size=4194304 esp32wrover.menu.FlashMode.qio=QIO esp32wrover.menu.FlashMode.qio.build.flash_mode=dio @@ -2153,7 +2326,7 @@ pico32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs pico32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 pico32.menu.PartitionScheme.custom=Custom pico32.menu.PartitionScheme.custom.build.partitions= -pico32.menu.PartitionScheme.custom.upload.maximum_size=16777216 +pico32.menu.PartitionScheme.custom.upload.maximum_size=4194304 pico32.menu.UploadSpeed.921600=921600 pico32.menu.UploadSpeed.921600.upload.speed=921600 @@ -2278,12 +2451,11 @@ esp32s3-octal.menu.FlashMode.dio.build.boot_freq=80m esp32s3-octal.menu.FlashMode.dio.build.flash_freq=80m esp32s3-octal.menu.FlashSize.16M=16MB (128Mb) +esp32s3-octal.menu.FlashSize.16M.build.flash_size=16MB esp32s3-octal.menu.FlashSize.4M=4MB (32Mb) esp32s3-octal.menu.FlashSize.4M.build.flash_size=4MB esp32s3-octal.menu.FlashSize.8M=8MB (64Mb) esp32s3-octal.menu.FlashSize.8M.build.flash_size=8MB -esp32s3-octal.menu.FlashSize.16M=16MB (128Mb) -esp32s3-octal.menu.FlashSize.16M.build.flash_size=16MB esp32s3-octal.menu.FlashSize.32M=32MB (256Mb) esp32s3-octal.menu.FlashSize.32M.build.flash_size=32MB @@ -2325,6 +2497,8 @@ esp32s3-octal.menu.UploadMode.cdc.upload.use_1200bps_touch=true esp32s3-octal.menu.UploadMode.cdc.upload.wait_for_upload_port=true esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 esp32s3-octal.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) esp32s3-octal.menu.PartitionScheme.default.build.partitions=default esp32s3-octal.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -2349,15 +2523,12 @@ esp32s3-octal.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s3-octal.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s3-octal.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s3-octal.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s3-octal.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s3-octal.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s3-octal.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s3-octal.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s3-octal.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) esp32s3-octal.menu.PartitionScheme.fatflash.build.partitions=ffat esp32s3-octal.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -esp32s3-octal.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 esp32s3-octal.menu.PartitionScheme.rainmaker=RainMaker 4MB esp32s3-octal.menu.PartitionScheme.rainmaker.build.partitions=rainmaker esp32s3-octal.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -2603,18 +2774,12 @@ esp32s3usbotg.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s3usbotg.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s3usbotg.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s3usbotg.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s3usbotg.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s3usbotg.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s3usbotg.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s3usbotg.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -esp32s3usbotg.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -esp32s3usbotg.menu.PartitionScheme.fatflash.build.partitions=ffat -esp32s3usbotg.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -esp32s3usbotg.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -esp32s3usbotg.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -esp32s3usbotg.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 esp32s3usbotg.menu.PartitionScheme.custom=Custom esp32s3usbotg.menu.PartitionScheme.custom.build.partitions= -esp32s3usbotg.menu.PartitionScheme.custom.upload.maximum_size=16777216 +esp32s3usbotg.menu.PartitionScheme.custom.upload.maximum_size=8388608 esp32s3usbotg.menu.DebugLevel.none=None esp32s3usbotg.menu.DebugLevel.none.build.code_debug=0 @@ -2692,9 +2857,6 @@ esp32s3camlcd.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1. esp32s3camlcd.menu.PartitionScheme.default.build.partitions=default esp32s3camlcd.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) esp32s3camlcd.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -esp32s3camlcd.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -esp32s3camlcd.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -esp32s3camlcd.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 esp32s3camlcd.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) esp32s3camlcd.menu.PartitionScheme.minimal.build.partitions=minimal esp32s3camlcd.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -2712,15 +2874,9 @@ esp32s3camlcd.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s3camlcd.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s3camlcd.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s3camlcd.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s3camlcd.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s3camlcd.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s3camlcd.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s3camlcd.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -esp32s3camlcd.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -esp32s3camlcd.menu.PartitionScheme.fatflash.build.partitions=ffat -esp32s3camlcd.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -esp32s3camlcd.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -esp32s3camlcd.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -esp32s3camlcd.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 esp32s3camlcd.menu.UploadSpeed.921600=921600 esp32s3camlcd.menu.UploadSpeed.921600.upload.speed=921600 @@ -2836,7 +2992,7 @@ esp32s2usb.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s2usb.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s2usb.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s2usb.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s2usb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s2usb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s2usb.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s2usb.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s2usb.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -2939,7 +3095,7 @@ esp32wroverkit.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32wroverkit.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32wroverkit.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32wroverkit.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32wroverkit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32wroverkit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32wroverkit.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32wroverkit.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32wroverkit.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -3171,7 +3327,7 @@ aventen_s3_sync.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 aventen_s3_sync.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) aventen_s3_sync.menu.PartitionScheme.huge_app.build.partitions=huge_app aventen_s3_sync.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -aventen_s3_sync.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +aventen_s3_sync.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) aventen_s3_sync.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs aventen_s3_sync.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 aventen_s3_sync.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -3297,7 +3453,7 @@ BharatPi-Node-Wifi.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 BharatPi-Node-Wifi.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) BharatPi-Node-Wifi.menu.PartitionScheme.huge_app.build.partitions=huge_app BharatPi-Node-Wifi.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -BharatPi-Node-Wifi.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +BharatPi-Node-Wifi.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) BharatPi-Node-Wifi.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs BharatPi-Node-Wifi.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 BharatPi-Node-Wifi.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -3455,7 +3611,7 @@ BharatPi-A7672S-4G.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 BharatPi-A7672S-4G.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) BharatPi-A7672S-4G.menu.PartitionScheme.huge_app.build.partitions=huge_app BharatPi-A7672S-4G.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -BharatPi-A7672S-4G.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +BharatPi-A7672S-4G.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) BharatPi-A7672S-4G.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs BharatPi-A7672S-4G.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 BharatPi-A7672S-4G.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -3613,7 +3769,7 @@ BharatPi-LoRa.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 BharatPi-LoRa.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) BharatPi-LoRa.menu.PartitionScheme.huge_app.build.partitions=huge_app BharatPi-LoRa.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -BharatPi-LoRa.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +BharatPi-LoRa.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) BharatPi-LoRa.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs BharatPi-LoRa.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 BharatPi-LoRa.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -4106,7 +4262,7 @@ um_feathers2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 um_feathers2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) um_feathers2.menu.PartitionScheme.huge_app.build.partitions=huge_app um_feathers2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -um_feathers2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +um_feathers2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) um_feathers2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs um_feathers2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -4248,7 +4404,7 @@ um_feathers2neo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 um_feathers2neo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) um_feathers2neo.menu.PartitionScheme.huge_app.build.partitions=huge_app um_feathers2neo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -um_feathers2neo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +um_feathers2neo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) um_feathers2neo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs um_feathers2neo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -4576,7 +4732,7 @@ um_feathers3neo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 um_feathers3neo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) um_feathers3neo.menu.PartitionScheme.huge_app.build.partitions=huge_app um_feathers3neo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -um_feathers3neo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +um_feathers3neo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) um_feathers3neo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs um_feathers3neo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -5388,7 +5544,7 @@ um_tinyc6.build.bootloader_addr=0x0 um_tinyc6.build.cdc_on_boot=1 um_tinyc6.build.f_cpu=160000000L -um_tinyc6.build.flash_size=4MB +um_tinyc6.build.flash_size=8MB um_tinyc6.build.flash_freq=80m um_tinyc6.build.flash_mode=qio um_tinyc6.build.boot=qio @@ -5433,7 +5589,7 @@ um_tinyc6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr um_tinyc6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 um_tinyc6.menu.PartitionScheme.custom=Custom um_tinyc6.menu.PartitionScheme.custom.build.partitions= -um_tinyc6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +um_tinyc6.menu.PartitionScheme.custom.upload.maximum_size=8388608 um_tinyc6.menu.CPUFreq.160=160MHz (WiFi) um_tinyc6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -5589,7 +5745,7 @@ um_tinys2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 um_tinys2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) um_tinys2.menu.PartitionScheme.huge_app.build.partitions=huge_app um_tinys2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -um_tinys2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +um_tinys2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) um_tinys2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs um_tinys2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -5861,6 +6017,8 @@ S_ODI_Ultra.menu.DebugLevel.info=Info S_ODI_Ultra.menu.DebugLevel.info.build.code_debug=3 S_ODI_Ultra.menu.DebugLevel.debug=Debug S_ODI_Ultra.menu.DebugLevel.debug.build.code_debug=4 +S_ODI_Ultra.menu.DebugLevel.verbose=Verbose +S_ODI_Ultra.menu.DebugLevel.verbose.build.code_debug=5 S_ODI_Ultra.menu.EraseFlash.none=Disabled S_ODI_Ultra.menu.EraseFlash.none.upload.erase_cmd= @@ -5939,7 +6097,7 @@ lilygo_t_display.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lilygo_t_display.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lilygo_t_display.menu.PartitionScheme.huge_app.build.partitions=huge_app lilygo_t_display.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lilygo_t_display.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lilygo_t_display.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lilygo_t_display.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lilygo_t_display.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 lilygo_t_display.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -6365,7 +6523,7 @@ lilygo_t3s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lilygo_t3s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lilygo_t3s3.menu.PartitionScheme.huge_app.build.partitions=huge_app lilygo_t3s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lilygo_t3s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lilygo_t3s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lilygo_t3s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lilygo_t3s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -7031,7 +7189,7 @@ micros2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 micros2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) micros2.menu.PartitionScheme.huge_app.build.partitions=huge_app micros2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -micros2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +micros2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) micros2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs micros2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -7349,7 +7507,7 @@ ttgo-t1.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ttgo-t1.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ttgo-t1.menu.PartitionScheme.huge_app.build.partitions=huge_app ttgo-t1.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ttgo-t1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ttgo-t1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ttgo-t1.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ttgo-t1.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -7479,7 +7637,7 @@ ttgo-t7-v13-mini32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ttgo-t7-v13-mini32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ttgo-t7-v13-mini32.menu.PartitionScheme.huge_app.build.partitions=huge_app ttgo-t7-v13-mini32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ttgo-t7-v13-mini32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ttgo-t7-v13-mini32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ttgo-t7-v13-mini32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ttgo-t7-v13-mini32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -7605,7 +7763,7 @@ ttgo-t7-v14-mini32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ttgo-t7-v14-mini32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ttgo-t7-v14-mini32.menu.PartitionScheme.huge_app.build.partitions=huge_app ttgo-t7-v14-mini32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ttgo-t7-v14-mini32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ttgo-t7-v14-mini32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ttgo-t7-v14-mini32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ttgo-t7-v14-mini32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -7731,7 +7889,7 @@ ttgo-t-oi-plus.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ttgo-t-oi-plus.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ttgo-t-oi-plus.menu.PartitionScheme.huge_app.build.partitions=huge_app ttgo-t-oi-plus.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ttgo-t-oi-plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ttgo-t-oi-plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ttgo-t-oi-plus.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ttgo-t-oi-plus.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -8205,7 +8363,7 @@ sparkfun_esp32s2_thing_plus.menu.PartitionScheme.noota_3gffat.upload.maximum_siz sparkfun_esp32s2_thing_plus.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_esp32s2_thing_plus.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_esp32s2_thing_plus.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_esp32s2_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_esp32s2_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_esp32s2_thing_plus.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_esp32s2_thing_plus.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 sparkfun_esp32s2_thing_plus.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -8430,7 +8588,7 @@ sparkfun_esp32s3_thing_plus.menu.PartitionScheme.noota_3gffat.upload.maximum_siz sparkfun_esp32s3_thing_plus.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_esp32s3_thing_plus.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_esp32s3_thing_plus.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_esp32s3_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_esp32s3_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_esp32s3_thing_plus.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_esp32s3_thing_plus.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 sparkfun_esp32s3_thing_plus.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -8444,7 +8602,7 @@ sparkfun_esp32s3_thing_plus.menu.PartitionScheme.zigbee_zczr.build.partitions=zi sparkfun_esp32s3_thing_plus.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 sparkfun_esp32s3_thing_plus.menu.PartitionScheme.custom=Custom sparkfun_esp32s3_thing_plus.menu.PartitionScheme.custom.build.partitions= -sparkfun_esp32s3_thing_plus.menu.PartitionScheme.custom.upload.maximum_size=16777216 +sparkfun_esp32s3_thing_plus.menu.PartitionScheme.custom.upload.maximum_size=4194304 sparkfun_esp32s3_thing_plus.menu.CPUFreq.240=240MHz (WiFi) sparkfun_esp32s3_thing_plus.menu.CPUFreq.240.build.f_cpu=240000000L @@ -8579,7 +8737,7 @@ sparkfun_esp32c6_thing_plus.menu.PartitionScheme.noota_3gffat.upload.maximum_siz sparkfun_esp32c6_thing_plus.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_esp32c6_thing_plus.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_esp32c6_thing_plus.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_esp32c6_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_esp32c6_thing_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_esp32c6_thing_plus.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_esp32c6_thing_plus.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 sparkfun_esp32c6_thing_plus.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -8747,7 +8905,7 @@ esp32micromod.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32micromod.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32micromod.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32micromod.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32micromod.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32micromod.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32micromod.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32micromod.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32micromod.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -8989,7 +9147,7 @@ sparkfun_esp32_iot_redboard.menu.PartitionScheme.noota_3gffat.upload.maximum_siz sparkfun_esp32_iot_redboard.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_esp32_iot_redboard.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_esp32_iot_redboard.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_esp32_iot_redboard.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_esp32_iot_redboard.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_esp32_iot_redboard.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_esp32_iot_redboard.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 sparkfun_esp32_iot_redboard.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -9169,7 +9327,7 @@ sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.noota_3gffat.upload.maximum_s sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 sparkfun_esp32c6_qwiic_pocket.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -9352,7 +9510,7 @@ sparkfun_pro_micro_esp32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size sparkfun_pro_micro_esp32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparkfun_pro_micro_esp32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app sparkfun_pro_micro_esp32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparkfun_pro_micro_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparkfun_pro_micro_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparkfun_pro_micro_esp32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparkfun_pro_micro_esp32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -9494,7 +9652,7 @@ nina_w10.menu.PartitionScheme.defaultffat.build.partitions=default_ffat nina_w10.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) nina_w10.menu.PartitionScheme.default_8MB.build.partitions=default_8MB nina_w10.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 -nina_w10.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +nina_w10.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) nina_w10.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs nina_w10.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 nina_w10.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -9714,7 +9872,7 @@ nora_w10.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 nora_w10.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) nora_w10.menu.PartitionScheme.huge_app.build.partitions=huge_app nora_w10.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -nora_w10.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +nora_w10.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) nora_w10.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs nora_w10.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 #nora_w10.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -10440,8 +10598,6 @@ lolin_s2_mini.build.flash_freq=80m lolin_s2_mini.build.flash_mode=dio lolin_s2_mini.build.boot=qio lolin_s2_mini.build.partitions=default -lolin_s2_mini.build.defines= - lolin_s2_mini.build.defines=-DBOARD_HAS_PSRAM lolin_s2_mini.menu.CDCOnBoot.default=Enabled @@ -10538,8 +10694,6 @@ lolin_s2_pico.build.flash_freq=80m lolin_s2_pico.build.flash_mode=dio lolin_s2_pico.build.boot=qio lolin_s2_pico.build.partitions=default -lolin_s2_pico.build.defines= - lolin_s2_pico.build.defines=-DBOARD_HAS_PSRAM lolin_s2_pico.menu.CDCOnBoot.default=Enabled @@ -10866,7 +11020,7 @@ lolin_s3_mini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lolin_s3_mini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lolin_s3_mini.menu.PartitionScheme.huge_app.build.partitions=huge_app lolin_s3_mini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lolin_s3_mini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lolin_s3_mini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lolin_s3_mini.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lolin_s3_mini.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 lolin_s3_mini.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -11037,7 +11191,7 @@ lolin_s3_mini_pro.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lolin_s3_mini_pro.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lolin_s3_mini_pro.menu.PartitionScheme.huge_app.build.partitions=huge_app lolin_s3_mini_pro.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lolin_s3_mini_pro.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lolin_s3_mini_pro.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lolin_s3_mini_pro.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lolin_s3_mini_pro.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 lolin_s3_mini_pro.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -11774,7 +11928,7 @@ WeMosBat.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 WeMosBat.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) WeMosBat.menu.PartitionScheme.huge_app.build.partitions=huge_app WeMosBat.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -WeMosBat.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +WeMosBat.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) WeMosBat.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs WeMosBat.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 WeMosBat.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -12243,9 +12397,6 @@ dfrobot_beetle_esp32c3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2 dfrobot_beetle_esp32c3.menu.PartitionScheme.default.build.partitions=default dfrobot_beetle_esp32c3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) dfrobot_beetle_esp32c3.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -dfrobot_beetle_esp32c3.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -dfrobot_beetle_esp32c3.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -dfrobot_beetle_esp32c3.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 dfrobot_beetle_esp32c3.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) dfrobot_beetle_esp32c3.menu.PartitionScheme.minimal.build.partitions=minimal dfrobot_beetle_esp32c3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -12263,15 +12414,9 @@ dfrobot_beetle_esp32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104 dfrobot_beetle_esp32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_beetle_esp32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_beetle_esp32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_beetle_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_beetle_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_beetle_esp32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_beetle_esp32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -dfrobot_beetle_esp32c3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -dfrobot_beetle_esp32c3.menu.PartitionScheme.fatflash.build.partitions=ffat -dfrobot_beetle_esp32c3.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -dfrobot_beetle_esp32c3.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -dfrobot_beetle_esp32c3.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -dfrobot_beetle_esp32c3.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 dfrobot_beetle_esp32c3.menu.PartitionScheme.rainmaker=RainMaker 4MB dfrobot_beetle_esp32c3.menu.PartitionScheme.rainmaker.build.partitions=rainmaker dfrobot_beetle_esp32c3.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -12415,7 +12560,7 @@ dfrobot_beetle_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104 dfrobot_beetle_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_beetle_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_beetle_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_beetle_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_beetle_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_beetle_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_beetle_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_beetle_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -12432,7 +12577,7 @@ dfrobot_beetle_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_ dfrobot_beetle_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 dfrobot_beetle_esp32c6.menu.PartitionScheme.custom=Custom dfrobot_beetle_esp32c6.menu.PartitionScheme.custom.build.partitions= -dfrobot_beetle_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +dfrobot_beetle_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 dfrobot_beetle_esp32c6.menu.CPUFreq.160=160MHz (WiFi) dfrobot_beetle_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -12566,7 +12711,7 @@ dfrobot_firebeetle2_esp32e.menu.PartitionScheme.noota_3gffat.upload.maximum_size dfrobot_firebeetle2_esp32e.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_firebeetle2_esp32e.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_firebeetle2_esp32e.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_firebeetle2_esp32e.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_firebeetle2_esp32e.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_firebeetle2_esp32e.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_firebeetle2_esp32e.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_firebeetle2_esp32e.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -12819,7 +12964,7 @@ dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_siz dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_firebeetle2_esp32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -12961,7 +13106,7 @@ dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_siz dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -12978,7 +13123,7 @@ dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zi dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.custom=Custom dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.custom.build.partitions= -dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +dfrobot_firebeetle2_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 dfrobot_firebeetle2_esp32c6.menu.CPUFreq.160=160MHz (WiFi) dfrobot_firebeetle2_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -13186,7 +13331,7 @@ dfrobot_romeo_esp32s3.menu.PartitionScheme.minimal.build.partitions=minimal dfrobot_romeo_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_romeo_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_romeo_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_romeo_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_romeo_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_romeo_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_romeo_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_romeo_esp32s3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -13373,7 +13518,7 @@ dfrobot_lorawan_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10 dfrobot_lorawan_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dfrobot_lorawan_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app dfrobot_lorawan_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dfrobot_lorawan_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dfrobot_lorawan_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dfrobot_lorawan_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dfrobot_lorawan_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 dfrobot_lorawan_esp32s3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -13747,16 +13892,16 @@ adafruit_metro_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_metro_esp32s2.menu.PSRAM.disabled=Disabled adafruit_metro_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_metro_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_metro_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_metro_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -13778,7 +13923,7 @@ adafruit_metro_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104 adafruit_metro_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_metro_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_metro_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_metro_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_metro_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_metro_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_metro_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -14133,16 +14278,16 @@ adafruit_magtag29_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_magtag29_esp32s2.menu.PSRAM.disabled=Disabled adafruit_magtag29_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_magtag29_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_magtag29_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14164,7 +14309,7 @@ adafruit_magtag29_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size= adafruit_magtag29_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_magtag29_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_magtag29_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_magtag29_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_magtag29_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -14316,16 +14461,16 @@ adafruit_funhouse_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_funhouse_esp32s2.menu.PSRAM.disabled=Disabled adafruit_funhouse_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_funhouse_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_funhouse_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14347,7 +14492,7 @@ adafruit_funhouse_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size= adafruit_funhouse_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_funhouse_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_funhouse_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_funhouse_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_funhouse_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -14488,7 +14633,7 @@ featheresp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 featheresp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) featheresp32.menu.PartitionScheme.huge_app.build.partitions=huge_app featheresp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -featheresp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +featheresp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) featheresp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs featheresp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -14754,16 +14899,16 @@ adafruit_feather_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s2.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14785,7 +14930,7 @@ adafruit_feather_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 adafruit_feather_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -14937,16 +15082,16 @@ adafruit_feather_esp32s2_tft.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s2_tft.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2_tft.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_tft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2_tft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14968,7 +15113,7 @@ adafruit_feather_esp32s2_tft.menu.PartitionScheme.noota_3gffat.upload.maximum_si adafruit_feather_esp32s2_tft.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s2_tft.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s2_tft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s2_tft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s2_tft.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -15120,16 +15265,16 @@ adafruit_feather_esp32s2_reversetft.menu.PSRAM.enabled.build.defines=-DBOARD_HAS adafruit_feather_esp32s2_reversetft.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2_reversetft.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15151,7 +15296,7 @@ adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.noota_3gffat.upload.max adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -15329,16 +15474,16 @@ adafruit_feather_esp32s3.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15360,7 +15505,7 @@ adafruit_feather_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 adafruit_feather_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -15734,16 +15879,16 @@ adafruit_feather_esp32s3_tft.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3_tft.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3_tft.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_tft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3_tft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15765,7 +15910,7 @@ adafruit_feather_esp32s3_tft.menu.PartitionScheme.noota_3gffat.upload.maximum_si adafruit_feather_esp32s3_tft.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s3_tft.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s3_tft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s3_tft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s3_tft.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -15952,16 +16097,16 @@ adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15983,7 +16128,7 @@ adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.noota_3gffat.upload.max adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -16126,9 +16271,6 @@ adafruit_feather_esp32c6.menu.PartitionScheme.default=Default 4MB with spiffs (1 adafruit_feather_esp32c6.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32c6.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) adafruit_feather_esp32c6.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -adafruit_feather_esp32c6.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -adafruit_feather_esp32c6.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -adafruit_feather_esp32c6.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 adafruit_feather_esp32c6.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) adafruit_feather_esp32c6.menu.PartitionScheme.minimal.build.partitions=minimal adafruit_feather_esp32c6.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -16146,15 +16288,9 @@ adafruit_feather_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 adafruit_feather_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_feather_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_feather_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_feather_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_feather_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_feather_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_feather_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -adafruit_feather_esp32c6.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -adafruit_feather_esp32c6.menu.PartitionScheme.fatflash.build.partitions=ffat -adafruit_feather_esp32c6.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -adafruit_feather_esp32c6.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -adafruit_feather_esp32c6.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -adafruit_feather_esp32c6.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 adafruit_feather_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB adafruit_feather_esp32c6.menu.PartitionScheme.rainmaker.build.partitions=rainmaker adafruit_feather_esp32c6.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -16169,7 +16305,7 @@ adafruit_feather_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbe adafruit_feather_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 adafruit_feather_esp32c6.menu.PartitionScheme.custom=Custom adafruit_feather_esp32c6.menu.PartitionScheme.custom.build.partitions= -adafruit_feather_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +adafruit_feather_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 adafruit_feather_esp32c6.menu.CPUFreq.160=160MHz (WiFi) adafruit_feather_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -16425,7 +16561,7 @@ adafruit_qtpy_esp32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048 adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_qtpy_esp32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_qtpy_esp32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -16575,16 +16711,16 @@ adafruit_qtpy_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_qtpy_esp32s2.menu.PSRAM.disabled=Disabled adafruit_qtpy_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_qtpy_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -16606,7 +16742,7 @@ adafruit_qtpy_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048 adafruit_qtpy_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_qtpy_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_qtpy_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_qtpy_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_qtpy_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -16971,16 +17107,16 @@ adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi=OPI PSRAM adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi.build.psram_type=opi -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.default.build.partitions=default adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -17002,7 +17138,7 @@ adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.noota_3gffat.upload.maximum_size adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -17535,7 +17671,7 @@ adafruit_camera_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10 adafruit_camera_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) adafruit_camera_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app adafruit_camera_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -adafruit_camera_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +adafruit_camera_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) adafruit_camera_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs adafruit_camera_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -17888,7 +18024,7 @@ sparklemotion.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 sparklemotion.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparklemotion.menu.PartitionScheme.huge_app.build.partitions=huge_app sparklemotion.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparklemotion.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparklemotion.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparklemotion.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparklemotion.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -18022,7 +18158,7 @@ sparklemotionmini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 sparklemotionmini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparklemotionmini.menu.PartitionScheme.huge_app.build.partitions=huge_app sparklemotionmini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparklemotionmini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparklemotionmini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparklemotionmini.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparklemotionmini.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -18156,7 +18292,7 @@ sparklemotionstick.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 sparklemotionstick.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sparklemotionstick.menu.PartitionScheme.huge_app.build.partitions=huge_app sparklemotionstick.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sparklemotionstick.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sparklemotionstick.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sparklemotionstick.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sparklemotionstick.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -18354,9 +18490,6 @@ nologo_esp32c3_super_mini.menu.PartitionScheme.default=Default 4MB with spiffs ( nologo_esp32c3_super_mini.menu.PartitionScheme.default.build.partitions=default nologo_esp32c3_super_mini.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) nologo_esp32c3_super_mini.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -nologo_esp32c3_super_mini.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -nologo_esp32c3_super_mini.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -nologo_esp32c3_super_mini.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 nologo_esp32c3_super_mini.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) nologo_esp32c3_super_mini.menu.PartitionScheme.minimal.build.partitions=minimal nologo_esp32c3_super_mini.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -18587,7 +18720,7 @@ nologo_esp32s3_pico.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104857 nologo_esp32s3_pico.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) nologo_esp32s3_pico.menu.PartitionScheme.huge_app.build.partitions=huge_app nologo_esp32s3_pico.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -nologo_esp32s3_pico.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +nologo_esp32s3_pico.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) nologo_esp32s3_pico.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs nologo_esp32s3_pico.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 nologo_esp32s3_pico.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -18605,12 +18738,6 @@ nologo_esp32s3_pico.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=40386 nologo_esp32s3_pico.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB nologo_esp32s3_pico.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB nologo_esp32s3_pico.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 -nologo_esp32s3_pico.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) -nologo_esp32s3_pico.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB -nologo_esp32s3_pico.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 -nologo_esp32s3_pico.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) -nologo_esp32s3_pico.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB -nologo_esp32s3_pico.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 nologo_esp32s3_pico.menu.PartitionScheme.esp_sr_16=ESP SR 16M (3MB APP/7MB SPIFFS/2.9MB MODEL) nologo_esp32s3_pico.menu.PartitionScheme.esp_sr_16.upload.maximum_size=3145728 nologo_esp32s3_pico.menu.PartitionScheme.esp_sr_16.upload.extra_flags=0xD10000 {build.path}/srmodels.bin @@ -18975,6 +19102,8 @@ esp32doit-devkit-v1.menu.DebugLevel.info=Info esp32doit-devkit-v1.menu.DebugLevel.info.build.code_debug=3 esp32doit-devkit-v1.menu.DebugLevel.debug=Debug esp32doit-devkit-v1.menu.DebugLevel.debug.build.code_debug=4 +esp32doit-devkit-v1.menu.DebugLevel.verbose=Verbose +esp32doit-devkit-v1.menu.DebugLevel.verbose.build.code_debug=5 esp32doit-devkit-v1.menu.EraseFlash.none=Disabled esp32doit-devkit-v1.menu.EraseFlash.none.upload.erase_cmd= @@ -19306,7 +19435,7 @@ esp32-poe.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32-poe.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32-poe.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32-poe.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32-poe.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32-poe.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32-poe.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32-poe.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32-poe.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -19449,7 +19578,7 @@ esp32-poe-iso.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32-poe-iso.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32-poe-iso.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32-poe-iso.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32-poe-iso.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32-poe-iso.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32-poe-iso.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32-poe-iso.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32-poe-iso.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -19579,8 +19708,6 @@ esp32-devkitlipo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 esp32-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (Large APPS with OTA) esp32-devkitlipo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32-devkitlipo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -esp32-devkitlipo.menu.PartitionScheme.fatflash=16M Fat -esp32-devkitlipo.menu.PartitionScheme.fatflash.build.partitions=ffat esp32-devkitlipo.menu.FlashMode.qio=QIO esp32-devkitlipo.menu.FlashMode.qio.build.flash_mode=dio @@ -19727,7 +19854,7 @@ esp32s2-devkitlipo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s2-devkitlipo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s2-devkitlipo.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s2-devkitlipo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s2-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s2-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s2-devkitlipo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s2-devkitlipo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s2-devkitlipo.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -19922,7 +20049,7 @@ esp32s2-devkitlipo-usb.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104 esp32s2-devkitlipo-usb.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s2-devkitlipo-usb.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s2-devkitlipo-usb.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s2-devkitlipo-usb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s2-devkitlipo-usb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s2-devkitlipo-usb.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s2-devkitlipo-usb.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s2-devkitlipo-usb.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -20177,7 +20304,7 @@ esp32s3-devkitlipo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32s3-devkitlipo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32s3-devkitlipo.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32s3-devkitlipo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32s3-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32s3-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32s3-devkitlipo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32s3-devkitlipo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32s3-devkitlipo.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -20335,7 +20462,7 @@ esp32c3-devkitlipo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c3-devkitlipo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c3-devkitlipo.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c3-devkitlipo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c3-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c3-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c3-devkitlipo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c3-devkitlipo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c3-devkitlipo.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -20508,7 +20635,7 @@ esp32c6-evb.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c6-evb.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c6-evb.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c6-evb.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c6-evb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c6-evb.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c6-evb.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c6-evb.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c6-evb.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -20691,7 +20818,7 @@ esp32h2-devkitlipo.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32h2-devkitlipo.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32h2-devkitlipo.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32h2-devkitlipo.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32h2-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32h2-devkitlipo.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32h2-devkitlipo.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32h2-devkitlipo.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32h2-devkitlipo.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -20866,7 +20993,7 @@ esp32-sbc-fabgl.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32-sbc-fabgl.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32-sbc-fabgl.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32-sbc-fabgl.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32-sbc-fabgl.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32-sbc-fabgl.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32-sbc-fabgl.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32-sbc-fabgl.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32-sbc-fabgl.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21112,7 +21239,7 @@ m5stack_core.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_core.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_core.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_core.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_core.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_core.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_core.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_core.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_core.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21285,7 +21412,7 @@ m5stack_fire.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_fire.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_fire.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_fire.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_fire.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_fire.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_fire.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_fire.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_fire.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21459,7 +21586,7 @@ m5stack_core2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_core2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_core2.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_core2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_core2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_core2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_core2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_core2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_core2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21633,7 +21760,7 @@ m5stack_tough.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_tough.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_tough.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_tough.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_tough.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_tough.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_tough.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_tough.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_tough.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21800,7 +21927,7 @@ m5stack_station.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_station.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_station.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_station.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_station.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_station.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_station.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_station.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_station.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -21949,9 +22076,6 @@ m5stack_stickc.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1 m5stack_stickc.menu.PartitionScheme.default.build.partitions=default m5stack_stickc.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_stickc.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_stickc.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_stickc.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_stickc.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_stickc.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_stickc.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_stickc.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -21966,12 +22090,9 @@ m5stack_stickc.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 m5stack_stickc.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) m5stack_stickc.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat m5stack_stickc.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 -m5stack_stickc.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stickc.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stickc.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stickc.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_stickc.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_stickc.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_stickc.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_stickc.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_stickc.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_stickc.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -21980,7 +22101,7 @@ m5stack_stickc.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB m5stack_stickc.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_stickc.menu.PartitionScheme.custom=Custom m5stack_stickc.menu.PartitionScheme.custom.build.partitions= -m5stack_stickc.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_stickc.menu.PartitionScheme.custom.upload.maximum_size=4194304 m5stack_stickc.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_stickc.menu.CPUFreq.240.build.f_cpu=240000000L @@ -22104,9 +22225,6 @@ m5stack_stickc_plus.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB m5stack_stickc_plus.menu.PartitionScheme.default.build.partitions=default m5stack_stickc_plus.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_stickc_plus.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_stickc_plus.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_stickc_plus.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_stickc_plus.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_stickc_plus.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_stickc_plus.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_stickc_plus.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -22121,12 +22239,9 @@ m5stack_stickc_plus.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 m5stack_stickc_plus.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) m5stack_stickc_plus.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat m5stack_stickc_plus.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 -m5stack_stickc_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stickc_plus.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stickc_plus.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stickc_plus.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_stickc_plus.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_stickc_plus.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_stickc_plus.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_stickc_plus.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_stickc_plus.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_stickc_plus.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -22135,7 +22250,7 @@ m5stack_stickc_plus.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmake m5stack_stickc_plus.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_stickc_plus.menu.PartitionScheme.custom=Custom m5stack_stickc_plus.menu.PartitionScheme.custom.build.partitions= -m5stack_stickc_plus.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_stickc_plus.menu.PartitionScheme.custom.upload.maximum_size=4194304 m5stack_stickc_plus.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_stickc_plus.menu.CPUFreq.240.build.f_cpu=240000000L @@ -22282,12 +22397,9 @@ m5stack_stickc_plus2.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 m5stack_stickc_plus2.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) m5stack_stickc_plus2.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat m5stack_stickc_plus2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 -m5stack_stickc_plus2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stickc_plus2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stickc_plus2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stickc_plus2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_stickc_plus2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_stickc_plus2.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_stickc_plus2.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_stickc_plus2.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_stickc_plus2.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_stickc_plus2.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -22299,7 +22411,7 @@ m5stack_stickc_plus2.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmak m5stack_stickc_plus2.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 m5stack_stickc_plus2.menu.PartitionScheme.custom=Custom m5stack_stickc_plus2.menu.PartitionScheme.custom.build.partitions= -m5stack_stickc_plus2.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_stickc_plus2.menu.PartitionScheme.custom.upload.maximum_size=8388608 m5stack_stickc_plus2.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_stickc_plus2.menu.CPUFreq.240.build.f_cpu=240000000L @@ -22422,9 +22534,6 @@ m5stack_atom.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5 m5stack_atom.menu.PartitionScheme.default.build.partitions=default m5stack_atom.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_atom.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_atom.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_atom.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_atom.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_atom.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_atom.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_atom.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -22439,12 +22548,9 @@ m5stack_atom.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 m5stack_atom.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) m5stack_atom.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat m5stack_atom.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 -m5stack_atom.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_atom.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_atom.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_atom.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_atom.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_atom.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_atom.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_atom.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_atom.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_atom.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -22453,7 +22559,7 @@ m5stack_atom.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_n m5stack_atom.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_atom.menu.PartitionScheme.custom=Custom m5stack_atom.menu.PartitionScheme.custom.build.partitions= -m5stack_atom.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_atom.menu.PartitionScheme.custom.upload.maximum_size=4194304 m5stack_atom.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_atom.menu.CPUFreq.240.build.f_cpu=240000000L @@ -22686,15 +22792,9 @@ m5stack_atoms3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_atoms3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_atoms3.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_atoms3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_atoms3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_atoms3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_atoms3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_atoms3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_atoms3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_atoms3.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_atoms3.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -m5stack_atoms3.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -m5stack_atoms3.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -m5stack_atoms3.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 m5stack_atoms3.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_atoms3.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_atoms3.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -22704,19 +22804,9 @@ m5stack_atoms3.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_atoms3.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB m5stack_atoms3.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB m5stack_atoms3.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 -m5stack_atoms3.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) -m5stack_atoms3.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB -m5stack_atoms3.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 -m5stack_atoms3.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) -m5stack_atoms3.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB -m5stack_atoms3.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 -m5stack_atoms3.menu.PartitionScheme.esp_sr_16=ESP SR 16M (3MB APP/7MB SPIFFS/2.9MB MODEL) -m5stack_atoms3.menu.PartitionScheme.esp_sr_16.upload.maximum_size=3145728 -m5stack_atoms3.menu.PartitionScheme.esp_sr_16.upload.extra_flags=0xD10000 {build.path}/srmodels.bin -m5stack_atoms3.menu.PartitionScheme.esp_sr_16.build.partitions=esp_sr_16 m5stack_atoms3.menu.PartitionScheme.custom=Custom m5stack_atoms3.menu.PartitionScheme.custom.build.partitions= -m5stack_atoms3.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_atoms3.menu.PartitionScheme.custom.upload.maximum_size=8388608 m5stack_atoms3.menu.CPUFreq.240=240MHz (WiFi) m5stack_atoms3.menu.CPUFreq.240.build.f_cpu=240000000L @@ -22922,7 +23012,7 @@ m5stack_cores3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_cores3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_cores3.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_cores3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_cores3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_cores3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_cores3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_cores3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_cores3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -23006,6 +23096,187 @@ m5stack_cores3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +m5stack_tab5.name=M5Tab5 + +m5stack_tab5.bootloader.tool=esptool_py +m5stack_tab5.bootloader.tool.default=esptool_py + +m5stack_tab5.upload.tool=esptool_py +m5stack_tab5.upload.tool.default=esptool_py +m5stack_tab5.upload.tool.network=esp_ota + +m5stack_tab5.upload.maximum_size=1310720 +m5stack_tab5.upload.maximum_data_size=327680 +m5stack_tab5.upload.flags= +m5stack_tab5.upload.extra_flags= +m5stack_tab5.upload.use_1200bps_touch=false +m5stack_tab5.upload.wait_for_upload_port=false + +m5stack_tab5.serial.disableDTR=false +m5stack_tab5.serial.disableRTS=false + +m5stack_tab5.build.tarch=riscv32 +m5stack_tab5.build.target=esp +m5stack_tab5.build.mcu=esp32p4 +m5stack_tab5.build.core=esp32 +m5stack_tab5.build.variant=m5stack_tab5 +m5stack_tab5.build.board=M5STACK_TAB5 +m5stack_tab5.build.bootloader_addr=0x2000 + +m5stack_tab5.build.usb_mode=0 +m5stack_tab5.build.cdc_on_boot=0 +m5stack_tab5.build.msc_on_boot=0 +m5stack_tab5.build.dfu_on_boot=0 +m5stack_tab5.build.f_cpu=360000000L +m5stack_tab5.build.flash_size=16MB +m5stack_tab5.build.flash_freq=80m +m5stack_tab5.build.img_freq=80m +m5stack_tab5.build.flash_mode=qio +m5stack_tab5.build.boot=qio +m5stack_tab5.build.partitions=default +m5stack_tab5.build.defines= + +## IDE 2.0 Seems to not update the value +m5stack_tab5.menu.JTAGAdapter.default=Disabled +m5stack_tab5.menu.JTAGAdapter.default.build.copy_jtag_files=0 +m5stack_tab5.menu.JTAGAdapter.builtin=Integrated USB JTAG +m5stack_tab5.menu.JTAGAdapter.builtin.build.openocdscript=esp32p4-builtin.cfg +m5stack_tab5.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +m5stack_tab5.menu.JTAGAdapter.external=FTDI Adapter +m5stack_tab5.menu.JTAGAdapter.external.build.openocdscript=esp32p4-ftdi.cfg +m5stack_tab5.menu.JTAGAdapter.external.build.copy_jtag_files=1 +m5stack_tab5.menu.JTAGAdapter.bridge=ESP USB Bridge +m5stack_tab5.menu.JTAGAdapter.bridge.build.openocdscript=esp32p4-bridge.cfg +m5stack_tab5.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +m5stack_tab5.menu.PSRAM.enabled=Enabled +m5stack_tab5.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +m5stack_tab5.menu.PSRAM.disabled=Disabled +m5stack_tab5.menu.PSRAM.disabled.build.defines= + +m5stack_tab5.menu.USBMode.hwcdc=Hardware CDC and JTAG +m5stack_tab5.menu.USBMode.hwcdc.build.usb_mode=1 +m5stack_tab5.menu.USBMode.default=USB-OTG (TinyUSB) +m5stack_tab5.menu.USBMode.default.build.usb_mode=0 + +m5stack_tab5.menu.CDCOnBoot.cdc=Enabled +m5stack_tab5.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +m5stack_tab5.menu.CDCOnBoot.default=Disabled +m5stack_tab5.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +m5stack_tab5.menu.MSCOnBoot.default=Disabled +m5stack_tab5.menu.MSCOnBoot.default.build.msc_on_boot=0 +m5stack_tab5.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +m5stack_tab5.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +m5stack_tab5.menu.DFUOnBoot.default=Disabled +m5stack_tab5.menu.DFUOnBoot.default.build.dfu_on_boot=0 +m5stack_tab5.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +m5stack_tab5.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +m5stack_tab5.menu.UploadMode.default=UART0 / Hardware CDC +m5stack_tab5.menu.UploadMode.default.upload.use_1200bps_touch=false +m5stack_tab5.menu.UploadMode.default.upload.wait_for_upload_port=false +m5stack_tab5.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +m5stack_tab5.menu.UploadMode.cdc.upload.use_1200bps_touch=true +m5stack_tab5.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +m5stack_tab5.menu.PartitionScheme.default=Default (2 x 6.5 MB app, 3.6 MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.default.build.partitions=default_16MB +m5stack_tab5.menu.PartitionScheme.default.upload.maximum_size=6553600 +m5stack_tab5.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +m5stack_tab5.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +m5stack_tab5.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +m5stack_tab5.menu.PartitionScheme.minimal.build.partitions=minimal +m5stack_tab5.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +m5stack_tab5.menu.PartitionScheme.no_fs.build.partitions=no_fs +m5stack_tab5.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +m5stack_tab5.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.no_ota.build.partitions=no_ota +m5stack_tab5.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +m5stack_tab5.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +m5stack_tab5.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +m5stack_tab5.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +m5stack_tab5.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +m5stack_tab5.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +m5stack_tab5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +m5stack_tab5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.huge_app.build.partitions=huge_app +m5stack_tab5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +m5stack_tab5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +m5stack_tab5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +m5stack_tab5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +m5stack_tab5.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) +m5stack_tab5.menu.PartitionScheme.fatflash.build.partitions=ffat +m5stack_tab5.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 +m5stack_tab5.menu.PartitionScheme.custom=Custom +m5stack_tab5.menu.PartitionScheme.custom.build.partitions= +m5stack_tab5.menu.PartitionScheme.custom.upload.maximum_size=16777216 + +## From https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/kconfig.html#config-esp-default-cpu-freq-mhz +m5stack_tab5.menu.CPUFreq.360=360MHz +m5stack_tab5.menu.CPUFreq.360.build.f_cpu=360000000L +m5stack_tab5.menu.CPUFreq.40=40MHz +m5stack_tab5.menu.CPUFreq.40.build.f_cpu=40000000L + +m5stack_tab5.menu.FlashMode.qio=QIO +m5stack_tab5.menu.FlashMode.qio.build.flash_mode=dio +m5stack_tab5.menu.FlashMode.qio.build.boot=qio +m5stack_tab5.menu.FlashMode.dio=DIO +m5stack_tab5.menu.FlashMode.dio.build.flash_mode=dio +m5stack_tab5.menu.FlashMode.dio.build.boot=dio + +m5stack_tab5.menu.FlashFreq.80=80MHz +m5stack_tab5.menu.FlashFreq.80.build.flash_freq=80m +m5stack_tab5.menu.FlashFreq.40=40MHz +m5stack_tab5.menu.FlashFreq.40.build.flash_freq=40m + +m5stack_tab5.menu.FlashSize.16M=16MB (128Mb) +m5stack_tab5.menu.FlashSize.16M.build.flash_size=16MB + +m5stack_tab5.menu.UploadSpeed.921600=921600 +m5stack_tab5.menu.UploadSpeed.921600.upload.speed=921600 +m5stack_tab5.menu.UploadSpeed.115200=115200 +m5stack_tab5.menu.UploadSpeed.115200.upload.speed=115200 +m5stack_tab5.menu.UploadSpeed.256000.windows=256000 +m5stack_tab5.menu.UploadSpeed.256000.upload.speed=256000 +m5stack_tab5.menu.UploadSpeed.230400.windows.upload.speed=256000 +m5stack_tab5.menu.UploadSpeed.230400=230400 +m5stack_tab5.menu.UploadSpeed.230400.upload.speed=230400 +m5stack_tab5.menu.UploadSpeed.460800.linux=460800 +m5stack_tab5.menu.UploadSpeed.460800.macosx=460800 +m5stack_tab5.menu.UploadSpeed.460800.upload.speed=460800 +m5stack_tab5.menu.UploadSpeed.512000.windows=512000 +m5stack_tab5.menu.UploadSpeed.512000.upload.speed=512000 + +m5stack_tab5.menu.DebugLevel.none=None +m5stack_tab5.menu.DebugLevel.none.build.code_debug=0 +m5stack_tab5.menu.DebugLevel.error=Error +m5stack_tab5.menu.DebugLevel.error.build.code_debug=1 +m5stack_tab5.menu.DebugLevel.warn=Warn +m5stack_tab5.menu.DebugLevel.warn.build.code_debug=2 +m5stack_tab5.menu.DebugLevel.info=Info +m5stack_tab5.menu.DebugLevel.info.build.code_debug=3 +m5stack_tab5.menu.DebugLevel.debug=Debug +m5stack_tab5.menu.DebugLevel.debug.build.code_debug=4 +m5stack_tab5.menu.DebugLevel.verbose=Verbose +m5stack_tab5.menu.DebugLevel.verbose.build.code_debug=5 + +m5stack_tab5.menu.EraseFlash.none=Disabled +m5stack_tab5.menu.EraseFlash.none.upload.erase_cmd= +m5stack_tab5.menu.EraseFlash.all=Enabled +m5stack_tab5.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + + m5stack_timer_cam.name=M5TimerCAM m5stack_timer_cam.bootloader.tool=esptool_py @@ -23053,9 +23324,6 @@ m5stack_timer_cam.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB AP m5stack_timer_cam.menu.PartitionScheme.default.build.partitions=default m5stack_timer_cam.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_timer_cam.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_timer_cam.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_timer_cam.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_timer_cam.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_timer_cam.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_timer_cam.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_timer_cam.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -23073,7 +23341,7 @@ m5stack_timer_cam.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_timer_cam.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_timer_cam.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_timer_cam.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_timer_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_timer_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_timer_cam.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_timer_cam.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -23204,9 +23472,6 @@ m5stack_unit_cam.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP m5stack_unit_cam.menu.PartitionScheme.default.build.partitions=default m5stack_unit_cam.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_unit_cam.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_unit_cam.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_unit_cam.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_unit_cam.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_unit_cam.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_unit_cam.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_unit_cam.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -23224,7 +23489,7 @@ m5stack_unit_cam.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_unit_cam.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_unit_cam.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_unit_cam.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_unit_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_unit_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_unit_cam.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_unit_cam.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -23464,7 +23729,7 @@ m5stack_unit_cams3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_unit_cams3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_unit_cams3.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_unit_cams3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_unit_cams3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_unit_cams3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_unit_cams3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_unit_cams3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_unit_cams3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -23588,9 +23853,6 @@ m5stack_poe_cam.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/ m5stack_poe_cam.menu.PartitionScheme.default.build.partitions=default m5stack_poe_cam.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_poe_cam.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_poe_cam.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_poe_cam.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_poe_cam.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_poe_cam.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_poe_cam.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_poe_cam.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -23608,7 +23870,7 @@ m5stack_poe_cam.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_poe_cam.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_poe_cam.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_poe_cam.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_poe_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_poe_cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_poe_cam.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_poe_cam.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -23758,7 +24020,7 @@ m5stack_paper.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_paper.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_paper.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_paper.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_paper.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_paper.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_paper.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_paper.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_paper.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -23904,9 +24166,6 @@ m5stack_coreink.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/ m5stack_coreink.menu.PartitionScheme.default.build.partitions=default m5stack_coreink.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_coreink.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_coreink.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_coreink.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_coreink.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_coreink.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_coreink.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_coreink.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -23924,12 +24183,9 @@ m5stack_coreink.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_coreink.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_coreink.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_coreink.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_coreink.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_coreink.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_coreink.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_coreink.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_coreink.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_coreink.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_coreink.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_coreink.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_coreink.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_coreink.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -23938,7 +24194,7 @@ m5stack_coreink.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4M m5stack_coreink.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_coreink.menu.PartitionScheme.custom=Custom m5stack_coreink.menu.PartitionScheme.custom.build.partitions= -m5stack_coreink.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_coreink.menu.PartitionScheme.custom.upload.maximum_size=4194304 m5stack_coreink.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_coreink.menu.CPUFreq.240.build.f_cpu=240000000L @@ -24058,9 +24314,6 @@ m5stack_stamp_pico.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB A m5stack_stamp_pico.menu.PartitionScheme.default.build.partitions=default m5stack_stamp_pico.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) m5stack_stamp_pico.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -m5stack_stamp_pico.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -m5stack_stamp_pico.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -m5stack_stamp_pico.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 m5stack_stamp_pico.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) m5stack_stamp_pico.menu.PartitionScheme.minimal.build.partitions=minimal m5stack_stamp_pico.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -24078,12 +24331,9 @@ m5stack_stamp_pico.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_stamp_pico.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_stamp_pico.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_stamp_pico.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_stamp_pico.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stamp_pico.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stamp_pico.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stamp_pico.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -m5stack_stamp_pico.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -m5stack_stamp_pico.menu.PartitionScheme.fatflash.build.partitions=ffat -m5stack_stamp_pico.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 m5stack_stamp_pico.menu.PartitionScheme.rainmaker=RainMaker 4MB m5stack_stamp_pico.menu.PartitionScheme.rainmaker.build.partitions=rainmaker m5stack_stamp_pico.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -24092,7 +24342,7 @@ m5stack_stamp_pico.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker m5stack_stamp_pico.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 m5stack_stamp_pico.menu.PartitionScheme.custom=Custom m5stack_stamp_pico.menu.PartitionScheme.custom.build.partitions= -m5stack_stamp_pico.menu.PartitionScheme.custom.upload.maximum_size=16777216 +m5stack_stamp_pico.menu.PartitionScheme.custom.upload.maximum_size=4194304 m5stack_stamp_pico.menu.CPUFreq.240=240MHz (WiFi/BT) m5stack_stamp_pico.menu.CPUFreq.240.build.f_cpu=240000000L @@ -24247,7 +24497,7 @@ m5stack_stamp_c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_stamp_c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_stamp_c3.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_stamp_c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_stamp_c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stamp_c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stamp_c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stamp_c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -24476,7 +24726,7 @@ m5stack_stamp_s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_stamp_s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_stamp_s3.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_stamp_s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_stamp_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_stamp_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_stamp_s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_stamp_s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_stamp_s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -24713,7 +24963,7 @@ m5stack_capsule.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_capsule.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_capsule.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_capsule.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_capsule.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_capsule.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_capsule.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_capsule.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_capsule.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -24953,7 +25203,7 @@ m5stack_cardputer.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_cardputer.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_cardputer.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_cardputer.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_cardputer.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_cardputer.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_cardputer.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_cardputer.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_cardputer.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -25190,7 +25440,7 @@ m5stack_dial.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_dial.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_dial.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_dial.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_dial.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_dial.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_dial.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_dial.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_dial.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -25427,7 +25677,7 @@ m5stack_dinmeter.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 m5stack_dinmeter.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) m5stack_dinmeter.menu.PartitionScheme.huge_app.build.partitions=huge_app m5stack_dinmeter.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -m5stack_dinmeter.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_dinmeter.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) m5stack_dinmeter.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs m5stack_dinmeter.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 m5stack_dinmeter.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -26004,7 +26254,7 @@ heltec_wifi_lora_32.menu.CPUFreq.240.build.f_cpu=240000000L heltec_wifi_lora_32.menu.CPUFreq.160=160MHz (WiFi/BT) heltec_wifi_lora_32.menu.CPUFreq.160.build.f_cpu=160000000L heltec_wifi_lora_32.menu.CPUFreq.80=80MHz (WiFi/BT) -heltec_wifi_lora_32.menu.CPUFreq.160.build.f_cpu=80000000L +heltec_wifi_lora_32.menu.CPUFreq.80.build.f_cpu=80000000L heltec_wifi_lora_32.menu.UploadSpeed.921600=921600 heltec_wifi_lora_32.menu.UploadSpeed.921600.upload.speed=921600 @@ -28627,7 +28877,7 @@ alksesp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 alksesp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) alksesp32.menu.PartitionScheme.huge_app.build.partitions=huge_app alksesp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -alksesp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +alksesp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) alksesp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs alksesp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 alksesp32.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -28831,7 +29081,7 @@ wt32-eth01.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 wt32-eth01.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) wt32-eth01.menu.PartitionScheme.huge_app.build.partitions=huge_app wt32-eth01.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -wt32-eth01.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +wt32-eth01.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) wt32-eth01.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs wt32-eth01.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -29252,7 +29502,7 @@ bpi_leaf_s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 bpi_leaf_s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) bpi_leaf_s3.menu.PartitionScheme.huge_app.build.partitions=huge_app bpi_leaf_s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -bpi_leaf_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +bpi_leaf_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) bpi_leaf_s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs bpi_leaf_s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 bpi_leaf_s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -29346,7 +29596,7 @@ wesp32.build.board=WESP32 wesp32.build.f_cpu=240000000L wesp32.build.flash_mode=dio -wesp32.build.flash_size=4MB +wesp32.build.flash_size=16MB wesp32.build.boot=dio wesp32.build.partitions=default wesp32.build.defines= @@ -29356,6 +29606,23 @@ wesp32.menu.FlashFreq.80.build.flash_freq=80m wesp32.menu.FlashFreq.40=40MHz wesp32.menu.FlashFreq.40.build.flash_freq=40m +wesp32.menu.FlashSize.default=16MB (128Mb) rev 7+ +wesp32.menu.FlashSize.default.build.flash_size=16MB +wesp32.menu.FlashSize.default_4m_flash=4MB (32Mb) rev 5 and below +wesp32.menu.FlashSize.default_4m_flash.build.flash_size=4MB + +wesp32.menu.PartitionScheme.default=16M OTA with large SPIFFS (4.5MB APP/6.8MB SPIFFS) +wesp32.menu.PartitionScheme.default.build.partitions=large_spiffs_16MB +wesp32.menu.PartitionScheme.default.upload.maximum_size=4718592 +wesp32.menu.PartitionScheme.default_large_app=16M large OTA app with SPIFFS (6.2MB APP/3.3MB SPIFFS) +wesp32.menu.PartitionScheme.default_large_app.build.partitions=default_16MB +wesp32.menu.PartitionScheme.default_large_app.upload.maximum_size=6553600 +wesp32.menu.PartitionScheme.default_fatfs=16M OTA with large FATFS (3MB APP/9.8MB FATFS) +wesp32.menu.PartitionScheme.default_fatfs.build.partitions=app3M_fat9M_16MB +wesp32.menu.PartitionScheme.default_fatfs.upload.maximum_size=3145728 +wesp32.menu.PartitionScheme.default_4m_flash=4M OTA app with SPIFFS (1.25MB APP/1.3MB SPIFFS) +wesp32.menu.PartitionScheme.default_4m_flash.build.partitions=default + wesp32.menu.UploadSpeed.921600=921600 wesp32.menu.UploadSpeed.921600.upload.speed=921600 wesp32.menu.UploadSpeed.115200=115200 @@ -29391,6 +29658,96 @@ wesp32.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +mant1s.name=Silicognition ManT1S + +mant1s.bootloader.tool=esptool_py +mant1s.bootloader.tool.default=esptool_py + +mant1s.upload.tool=esptool_py +mant1s.upload.tool.default=esptool_py +mant1s.upload.tool.network=esp_ota + +mant1s.upload.maximum_size=1310720 +mant1s.upload.maximum_data_size=2424832 +mant1s.upload.flags= +mant1s.upload.extra_flags= + +mant1s.serial.disableDTR=true +mant1s.serial.disableRTS=true + +mant1s.build.tarch=xtensa +mant1s.build.bootloader_addr=0x1000 +mant1s.build.target=esp32 +mant1s.build.mcu=esp32 +mant1s.build.core=esp32 +mant1s.build.variant=mant1s +mant1s.build.board=MANT1S + +mant1s.build.f_cpu=240000000L +mant1s.build.flash_mode=dio +mant1s.build.flash_size=8MB +mant1s.build.boot=dio +mant1s.build.partitions=default +mant1s.build.defines= + +mant1s.menu.FlashFreq.80=80MHz +mant1s.menu.FlashFreq.80.build.flash_freq=80m +mant1s.menu.FlashFreq.40=40MHz +mant1s.menu.FlashFreq.40.build.flash_freq=40m + +mant1s.menu.PSRAM.enabled=Enabled +mant1s.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw +mant1s.menu.PSRAM.enabled.build.extra_libs= +mant1s.menu.PSRAM.disabled=Disabled +mant1s.menu.PSRAM.disabled.build.defines= +mant1s.menu.PSRAM.disabled.build.extra_libs= + +mant1s.menu.PartitionScheme.default=8M OTA with large SPIFFS (1.25MB APP/5.3MB SPIFFS) +mant1s.menu.PartitionScheme.default.build.partitions=large_spiffs_8MB +mant1s.menu.PartitionScheme.default_large_app=8M large OTA app with SPIFFS (3MB APP/1.5MB SPIFFS) +mant1s.menu.PartitionScheme.default_large_app.build.partitions=default_8MB +mant1s.menu.PartitionScheme.default_large_app.upload.maximum_size=3342336 +mant1s.menu.PartitionScheme.default_fatfs=8M OTA with large FATFS (1.25MB APP/5.3MB FATFS) +mant1s.menu.PartitionScheme.default_fatfs.build.partitions=large_ffat_8MB +mant1s.menu.PartitionScheme.default_large_app_fatfs=8M large OTA app with FATFS (3MB APP/1.5MB FATFS) +mant1s.menu.PartitionScheme.default_large_app_fatfs.build.partitions=default_ffat_8MB +mant1s.menu.PartitionScheme.default_large_app_fatfs.upload.maximum_size=3342336 + +mant1s.menu.UploadSpeed.921600=921600 +mant1s.menu.UploadSpeed.921600.upload.speed=921600 +mant1s.menu.UploadSpeed.115200=115200 +mant1s.menu.UploadSpeed.115200.upload.speed=115200 +mant1s.menu.UploadSpeed.256000.windows=256000 +mant1s.menu.UploadSpeed.256000.upload.speed=256000 +mant1s.menu.UploadSpeed.230400.windows.upload.speed=256000 +mant1s.menu.UploadSpeed.230400=230400 +mant1s.menu.UploadSpeed.230400.upload.speed=230400 +mant1s.menu.UploadSpeed.460800.linux=460800 +mant1s.menu.UploadSpeed.460800.macosx=460800 +mant1s.menu.UploadSpeed.460800.upload.speed=460800 +mant1s.menu.UploadSpeed.512000.windows=512000 +mant1s.menu.UploadSpeed.512000.upload.speed=512000 + +mant1s.menu.DebugLevel.none=None +mant1s.menu.DebugLevel.none.build.code_debug=0 +mant1s.menu.DebugLevel.error=Error +mant1s.menu.DebugLevel.error.build.code_debug=1 +mant1s.menu.DebugLevel.warn=Warn +mant1s.menu.DebugLevel.warn.build.code_debug=2 +mant1s.menu.DebugLevel.info=Info +mant1s.menu.DebugLevel.info.build.code_debug=3 +mant1s.menu.DebugLevel.debug=Debug +mant1s.menu.DebugLevel.debug.build.code_debug=4 +mant1s.menu.DebugLevel.verbose=Verbose +mant1s.menu.DebugLevel.verbose.build.code_debug=5 + +mant1s.menu.EraseFlash.none=Disabled +mant1s.menu.EraseFlash.none.upload.erase_cmd= +mant1s.menu.EraseFlash.all=Enabled +mant1s.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + t-beam.name=T-Beam t-beam.bootloader.tool=esptool_py @@ -30009,7 +30366,7 @@ fri3d_2024_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 fri3d_2024_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) fri3d_2024_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app fri3d_2024_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -fri3d_2024_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +fri3d_2024_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) fri3d_2024_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs fri3d_2024_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 fri3d_2024_esp32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -30234,7 +30591,7 @@ esp32cam.menu.FlashMode.dio.build.boot=dio esp32cam.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32cam.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32cam.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32cam.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32cam.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32cam.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32cam.menu.PartitionScheme.default=Regular 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) @@ -30722,7 +31079,7 @@ vintlabs-devkit-v1.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 vintlabs-devkit-v1.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) vintlabs-devkit-v1.menu.PartitionScheme.huge_app.build.partitions=huge_app vintlabs-devkit-v1.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -vintlabs-devkit-v1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +vintlabs-devkit-v1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) vintlabs-devkit-v1.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs vintlabs-devkit-v1.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 vintlabs-devkit-v1.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -30751,6 +31108,8 @@ vintlabs-devkit-v1.menu.DebugLevel.info=Info vintlabs-devkit-v1.menu.DebugLevel.info.build.code_debug=3 vintlabs-devkit-v1.menu.DebugLevel.debug=Debug vintlabs-devkit-v1.menu.DebugLevel.debug.build.code_debug=4 +vintlabs-devkit-v1.menu.DebugLevel.verbose=Verbose +vintlabs-devkit-v1.menu.DebugLevel.verbose.build.code_debug=5 vintlabs-devkit-v1.menu.EraseFlash.none=Disabled vintlabs-devkit-v1.menu.EraseFlash.none.upload.erase_cmd= @@ -30895,7 +31254,7 @@ mgbot-iotik32a.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 mgbot-iotik32a.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) mgbot-iotik32a.menu.PartitionScheme.huge_app.build.partitions=huge_app mgbot-iotik32a.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -mgbot-iotik32a.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +mgbot-iotik32a.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) mgbot-iotik32a.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs mgbot-iotik32a.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 mgbot-iotik32a.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -31042,7 +31401,7 @@ mgbot-iotik32b.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 mgbot-iotik32b.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) mgbot-iotik32b.menu.PartitionScheme.huge_app.build.partitions=huge_app mgbot-iotik32b.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -mgbot-iotik32b.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +mgbot-iotik32b.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) mgbot-iotik32b.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs mgbot-iotik32b.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 mgbot-iotik32b.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -31497,11 +31856,9 @@ mPython.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 mPython.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) mPython.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat mPython.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 -mPython.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +mPython.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) mPython.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs mPython.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -mPython.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -mPython.menu.PartitionScheme.fatflash.build.partitions=ffat mPython.menu.CPUFreq.240=240MHz (WiFi/BT) mPython.menu.CPUFreq.240.build.f_cpu=240000000L @@ -31776,7 +32133,7 @@ wifiduino32c3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 wifiduino32c3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) wifiduino32c3.menu.PartitionScheme.huge_app.build.partitions=huge_app wifiduino32c3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -wifiduino32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +wifiduino32c3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) wifiduino32c3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs wifiduino32c3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 wifiduino32c3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -32008,7 +32365,7 @@ wifiduino32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 wifiduino32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) wifiduino32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app wifiduino32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -wifiduino32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +wifiduino32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) wifiduino32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs wifiduino32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 wifiduino32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -32372,7 +32729,7 @@ ch_denky.menu.Revision.denkyd4.build.board=DENKY_PICOV3 ch_denky.menu.Revision.denkyd4.build.flash_size=8MB ch_denky.menu.Revision.denky32=WROOM32 ch_denky.menu.Revision.denky32.build.board=DENKY_WROOM32 -ch_denky.menu.Revision.denkyd4.build.flash_size=4MB +ch_denky.menu.Revision.denky32.build.flash_size=4MB ch_denky.menu.PartitionScheme.default=Default ch_denky.menu.PartitionScheme.default.build.partitions=default @@ -32479,7 +32836,7 @@ uPesy_wrover.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 uPesy_wrover.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) uPesy_wrover.menu.PartitionScheme.huge_app.build.partitions=huge_app uPesy_wrover.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -uPesy_wrover.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +uPesy_wrover.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) uPesy_wrover.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs uPesy_wrover.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -32596,7 +32953,7 @@ uPesy_wroom.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 uPesy_wroom.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) uPesy_wroom.menu.PartitionScheme.huge_app.build.partitions=huge_app uPesy_wroom.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -uPesy_wroom.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +uPesy_wroom.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) uPesy_wroom.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs uPesy_wroom.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -32706,7 +33063,7 @@ uPesy_edu_esp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 uPesy_edu_esp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) uPesy_edu_esp32.menu.PartitionScheme.huge_app.build.partitions=huge_app uPesy_edu_esp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -uPesy_edu_esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +uPesy_edu_esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) uPesy_edu_esp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs uPesy_edu_esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -33218,7 +33575,7 @@ kb32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 kb32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) kb32.menu.PartitionScheme.huge_app.build.partitions=huge_app kb32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -kb32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +kb32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) kb32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs kb32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 kb32.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -33396,7 +33753,7 @@ deneyapkart.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapkart.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapkart.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapkart.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapkart.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapkart.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapkart.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapkart.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapkart.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -33650,7 +34007,7 @@ deneyapkartv2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapkartv2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapkartv2.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapkartv2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapkartv2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapkartv2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapkartv2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapkartv2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapkartv2.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -33664,7 +34021,7 @@ deneyapkartv2.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr deneyapkartv2.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 deneyapkartv2.menu.PartitionScheme.custom=Custom deneyapkartv2.menu.PartitionScheme.custom.build.partitions= -deneyapkartv2.menu.PartitionScheme.custom.upload.maximum_size=16777216 +deneyapkartv2.menu.PartitionScheme.custom.upload.maximum_size=4194304 deneyapkartv2.menu.CPUFreq.240=240MHz (WiFi) deneyapkartv2.menu.CPUFreq.240.build.f_cpu=240000000L @@ -33797,7 +34154,7 @@ deneyapkart1A.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapkart1A.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapkart1A.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapkart1A.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapkart1A.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapkart1A.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapkart1A.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapkart1A.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapkart1A.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -34060,7 +34417,7 @@ deneyapkart1Av2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapkart1Av2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapkart1Av2.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapkart1Av2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapkart1Av2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapkart1Av2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapkart1Av2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapkart1Av2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapkart1Av2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -34229,7 +34586,7 @@ deneyapmini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapmini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapmini.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapmini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapmini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapmini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapmini.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapmini.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapmini.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -34419,7 +34776,7 @@ deneyapminiv2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapminiv2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapminiv2.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapminiv2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapminiv2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapminiv2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapminiv2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapminiv2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapminiv2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -34588,7 +34945,7 @@ deneyapkartg.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 deneyapkartg.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) deneyapkartg.menu.PartitionScheme.huge_app.build.partitions=huge_app deneyapkartg.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -deneyapkartg.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +deneyapkartg.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) deneyapkartg.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs deneyapkartg.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 deneyapkartg.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -34900,7 +35257,7 @@ atmegazero_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 atmegazero_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) atmegazero_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app atmegazero_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -atmegazero_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +atmegazero_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) atmegazero_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs atmegazero_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 atmegazero_esp32s2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -35056,7 +35413,7 @@ franzininho_wifi_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 franzininho_wifi_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) franzininho_wifi_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app franzininho_wifi_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -franzininho_wifi_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +franzininho_wifi_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) franzininho_wifi_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs franzininho_wifi_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 franzininho_wifi_esp32s2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -35163,7 +35520,7 @@ franzininho_wifi_msc_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_si franzininho_wifi_msc_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) franzininho_wifi_msc_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app franzininho_wifi_msc_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -franzininho_wifi_msc_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +franzininho_wifi_msc_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) franzininho_wifi_msc_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs franzininho_wifi_msc_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 franzininho_wifi_msc_esp32s2.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -35336,7 +35693,7 @@ tamc_termod_s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 tamc_termod_s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) tamc_termod_s3.menu.PartitionScheme.huge_app.build.partitions=huge_app tamc_termod_s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -tamc_termod_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +tamc_termod_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) tamc_termod_s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs tamc_termod_s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 tamc_termod_s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -35464,11 +35821,9 @@ dpu_esp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 dpu_esp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) dpu_esp32.menu.PartitionScheme.huge_app.build.partitions=huge_app dpu_esp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -dpu_esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +dpu_esp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) dpu_esp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs dpu_esp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -dpu_esp32.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -dpu_esp32.menu.PartitionScheme.fatflash.build.partitions=ffat dpu_esp32.menu.FlashMode.qio=QIO dpu_esp32.menu.FlashMode.qio.build.flash_mode=dio @@ -35685,7 +36040,7 @@ lionbit.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lionbit.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lionbit.menu.PartitionScheme.huge_app.build.partitions=huge_app lionbit.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lionbit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lionbit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lionbit.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lionbit.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 lionbit.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -35821,7 +36176,7 @@ watchy.menu.Revision.v20.build.board=WATCHY_V20 watchy.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) watchy.menu.PartitionScheme.huge_app.build.partitions=huge_app watchy.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -watchy.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +watchy.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) watchy.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs watchy.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -36034,7 +36389,7 @@ XIAO_ESP32C3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 XIAO_ESP32C3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) XIAO_ESP32C3.menu.PartitionScheme.huge_app.build.partitions=huge_app XIAO_ESP32C3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -XIAO_ESP32C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +XIAO_ESP32C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) XIAO_ESP32C3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs XIAO_ESP32C3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 XIAO_ESP32C3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -36120,6 +36475,197 @@ XIAO_ESP32C3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## + +XIAO_ESP32C5.name=XIAO_ESP32C5 + +XIAO_ESP32C5.vid.0=0x2886 +XIAO_ESP32C5.pid.0=0x0067 + +XIAO_ESP32C5.bootloader.tool=esptool_py +XIAO_ESP32C5.bootloader.tool.default=esptool_py + +XIAO_ESP32C5.upload.tool=esptool_py +XIAO_ESP32C5.upload.tool.default=esptool_py +XIAO_ESP32C5.upload.tool.network=esp_ota + +XIAO_ESP32C5.upload.maximum_size=1310720 +XIAO_ESP32C5.upload.maximum_data_size=327680 +XIAO_ESP32C5.upload.flags= +XIAO_ESP32C5.upload.extra_flags= +XIAO_ESP32C5.upload.use_1200bps_touch=false +XIAO_ESP32C5.upload.wait_for_upload_port=false + +XIAO_ESP32C5.serial.disableDTR=false +XIAO_ESP32C5.serial.disableRTS=false + +XIAO_ESP32C5.build.tarch=riscv32 +XIAO_ESP32C5.build.target=esp +XIAO_ESP32C5.build.mcu=esp32c5 +XIAO_ESP32C5.build.core=esp32 +XIAO_ESP32C5.build.variant=XIAO_ESP32C5 +XIAO_ESP32C5.build.board=XIAO_ESP32C5 +XIAO_ESP32C5.build.bootloader_addr=0x2000 + +XIAO_ESP32C5.build.cdc_on_boot=1 +XIAO_ESP32C5.build.f_cpu=240000000L +XIAO_ESP32C5.build.flash_size=8MB +XIAO_ESP32C5.build.flash_freq=80m +XIAO_ESP32C5.build.flash_mode=qio +XIAO_ESP32C5.build.boot=qio +XIAO_ESP32C5.build.partitions=default_8MB +XIAO_ESP32C5.build.defines= + +## IDE 2.0 Seems to not update the value +XIAO_ESP32C5.menu.JTAGAdapter.default=Disabled +XIAO_ESP32C5.menu.JTAGAdapter.default.build.copy_jtag_files=0 +XIAO_ESP32C5.menu.JTAGAdapter.builtin=Integrated USB JTAG +XIAO_ESP32C5.menu.JTAGAdapter.builtin.build.openocdscript=esp32c5-builtin.cfg +XIAO_ESP32C5.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +XIAO_ESP32C5.menu.JTAGAdapter.external=FTDI Adapter +XIAO_ESP32C5.menu.JTAGAdapter.external.build.openocdscript=esp32c5-ftdi.cfg +XIAO_ESP32C5.menu.JTAGAdapter.external.build.copy_jtag_files=1 +XIAO_ESP32C5.menu.JTAGAdapter.bridge=ESP USB Bridge +XIAO_ESP32C5.menu.JTAGAdapter.bridge.build.openocdscript=esp32c5-bridge.cfg +XIAO_ESP32C5.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +XIAO_ESP32C5.menu.PSRAM.disabled=Disabled +XIAO_ESP32C5.menu.PSRAM.disabled.build.defines= +XIAO_ESP32C5.menu.PSRAM.enabled=Enabled +XIAO_ESP32C5.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM + +XIAO_ESP32C5.menu.CDCOnBoot.cdc=Enabled +XIAO_ESP32C5.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +XIAO_ESP32C5.menu.CDCOnBoot.default=Disabled +XIAO_ESP32C5.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +XIAO_ESP32C5.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +XIAO_ESP32C5.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +XIAO_ESP32C5.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.default.build.partitions=default +XIAO_ESP32C5.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +XIAO_ESP32C5.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.minimal.build.partitions=minimal +XIAO_ESP32C5.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.no_ota.build.partitions=no_ota +XIAO_ESP32C5.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +XIAO_ESP32C5.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +XIAO_ESP32C5.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +XIAO_ESP32C5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.huge_app.build.partitions=huge_app +XIAO_ESP32C5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +XIAO_ESP32C5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +XIAO_ESP32C5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker=RainMaker 4MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +XIAO_ESP32C5.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480 +XIAO_ESP32C5.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee.build.partitions=zigbee +XIAO_ESP32C5.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB=Zigbee 8MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB.build.partitions=zigbee_8MB +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB.upload.maximum_size=3407872 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 + +XIAO_ESP32C5.menu.CPUFreq.240=240MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.240.build.f_cpu=240000000L +XIAO_ESP32C5.menu.CPUFreq.160=160MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.160.build.f_cpu=160000000L +XIAO_ESP32C5.menu.CPUFreq.80=80MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.80.build.f_cpu=80000000L +XIAO_ESP32C5.menu.CPUFreq.40=40MHz +XIAO_ESP32C5.menu.CPUFreq.40.build.f_cpu=40000000L +XIAO_ESP32C5.menu.CPUFreq.20=20MHz +XIAO_ESP32C5.menu.CPUFreq.20.build.f_cpu=20000000L +XIAO_ESP32C5.menu.CPUFreq.10=10MHz +XIAO_ESP32C5.menu.CPUFreq.10.build.f_cpu=10000000L + +XIAO_ESP32C5.menu.FlashMode.qio=QIO +XIAO_ESP32C5.menu.FlashMode.qio.build.flash_mode=dio +XIAO_ESP32C5.menu.FlashMode.qio.build.boot=qio +XIAO_ESP32C5.menu.FlashMode.dio=DIO +XIAO_ESP32C5.menu.FlashMode.dio.build.flash_mode=dio +XIAO_ESP32C5.menu.FlashMode.dio.build.boot=dio + +XIAO_ESP32C5.menu.FlashFreq.80=80MHz +XIAO_ESP32C5.menu.FlashFreq.80.build.flash_freq=80m +XIAO_ESP32C5.menu.FlashFreq.40=40MHz +XIAO_ESP32C5.menu.FlashFreq.40.build.flash_freq=40m + +XIAO_ESP32C5.menu.FlashSize.8M=8MB (64Mb) +XIAO_ESP32C5.menu.FlashSize.8M.build.flash_size=8MB + +XIAO_ESP32C5.menu.UploadSpeed.921600=921600 +XIAO_ESP32C5.menu.UploadSpeed.921600.upload.speed=921600 +XIAO_ESP32C5.menu.UploadSpeed.115200=115200 +XIAO_ESP32C5.menu.UploadSpeed.115200.upload.speed=115200 +XIAO_ESP32C5.menu.UploadSpeed.256000.windows=256000 +XIAO_ESP32C5.menu.UploadSpeed.256000.upload.speed=256000 +XIAO_ESP32C5.menu.UploadSpeed.230400.windows.upload.speed=256000 +XIAO_ESP32C5.menu.UploadSpeed.230400=230400 +XIAO_ESP32C5.menu.UploadSpeed.230400.upload.speed=230400 +XIAO_ESP32C5.menu.UploadSpeed.460800.linux=460800 +XIAO_ESP32C5.menu.UploadSpeed.460800.macosx=460800 +XIAO_ESP32C5.menu.UploadSpeed.460800.upload.speed=460800 +XIAO_ESP32C5.menu.UploadSpeed.512000.windows=512000 +XIAO_ESP32C5.menu.UploadSpeed.512000.upload.speed=512000 + +XIAO_ESP32C5.menu.DebugLevel.none=None +XIAO_ESP32C5.menu.DebugLevel.none.build.code_debug=0 +XIAO_ESP32C5.menu.DebugLevel.error=Error +XIAO_ESP32C5.menu.DebugLevel.error.build.code_debug=1 +XIAO_ESP32C5.menu.DebugLevel.warn=Warn +XIAO_ESP32C5.menu.DebugLevel.warn.build.code_debug=2 +XIAO_ESP32C5.menu.DebugLevel.info=Info +XIAO_ESP32C5.menu.DebugLevel.info.build.code_debug=3 +XIAO_ESP32C5.menu.DebugLevel.debug=Debug +XIAO_ESP32C5.menu.DebugLevel.debug.build.code_debug=4 +XIAO_ESP32C5.menu.DebugLevel.verbose=Verbose +XIAO_ESP32C5.menu.DebugLevel.verbose.build.code_debug=5 + +XIAO_ESP32C5.menu.EraseFlash.none=Disabled +XIAO_ESP32C5.menu.EraseFlash.none.upload.erase_cmd= +XIAO_ESP32C5.menu.EraseFlash.all=Enabled +XIAO_ESP32C5.menu.EraseFlash.all.upload.erase_cmd=-e + +XIAO_ESP32C5.menu.ZigbeeMode.default=Disabled +XIAO_ESP32C5.menu.ZigbeeMode.default.build.zigbee_mode= +XIAO_ESP32C5.menu.ZigbeeMode.default.build.zigbee_libs= +XIAO_ESP32C5.menu.ZigbeeMode.ed=Zigbee ED (end device) +XIAO_ESP32C5.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +XIAO_ESP32C5.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +XIAO_ESP32C5.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +XIAO_ESP32C5.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +XIAO_ESP32C5.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug=Zigbee ED (end device) - Debug +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug.build.zigbee_mode=-DZIGBEE_MODE_ED +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug.build.zigbee_libs=-lesp_zb_api.ed.debug -lzboss_stack.ed.debug -lzboss_port.native.debug +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug=Zigbee ZCZR (coordinator/router) - Debug +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug.build.zigbee_libs=-lesp_zb_api.zczr.debug -lzboss_stack.zczr.debug -lzboss_port.native.debug + +############################################################## + XIAO_ESP32C6.name=XIAO_ESP32C6 XIAO_ESP32C6.bootloader.tool=esptool_py @@ -36397,9 +36943,15 @@ XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536 XIAO_ESP32S3.menu.PartitionScheme.tinyuf2=TinyUF2 8MB (2MB APP/3.7MB FFAT) XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-8MB-tinyuf2 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-8MB XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota=TinyUF2 8MB No OTA (4MB APP/3.7MB FFAT) +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-8MB-noota +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=4194304 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" + XIAO_ESP32S3.menu.CPUFreq.240=240MHz (WiFi) XIAO_ESP32S3.menu.CPUFreq.240.build.f_cpu=240000000L @@ -36691,7 +37243,7 @@ connaxio_espoir.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 connaxio_espoir.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) connaxio_espoir.menu.PartitionScheme.huge_app.build.partitions=huge_app connaxio_espoir.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -connaxio_espoir.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +connaxio_espoir.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) connaxio_espoir.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs connaxio_espoir.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 connaxio_espoir.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -36924,7 +37476,7 @@ department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.noota_3gffat.upload. department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs department_of_alchemy_minimain_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -37037,8 +37589,6 @@ Bee_Data_Logger.build.f_cpu=240000000L Bee_Data_Logger.build.flash_size=8MB Bee_Data_Logger.build.flash_freq=80m Bee_Data_Logger.build.flash_mode=dio -Bee_Data_Logger.build.partitions=default_8MB -Bee_Data_Logger.build.defines= Bee_Data_Logger.build.loop_core=-DARDUINO_RUNNING_CORE=1 Bee_Data_Logger.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 Bee_Data_Logger.build.boot=qio @@ -37532,7 +38082,7 @@ Bee_S3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 Bee_S3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Bee_S3.menu.PartitionScheme.huge_app.build.partitions=huge_app Bee_S3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Bee_S3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +Bee_S3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) Bee_S3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs Bee_S3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 Bee_S3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -37774,7 +38324,7 @@ unphone8.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 unphone8.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) unphone8.menu.PartitionScheme.huge_app.build.partitions=huge_app unphone8.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -unphone8.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +unphone8.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) unphone8.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs unphone8.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 unphone8.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -37930,7 +38480,7 @@ unphone9.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 unphone9.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) unphone9.menu.PartitionScheme.huge_app.build.partitions=huge_app unphone9.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -unphone9.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +unphone9.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) unphone9.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs unphone9.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 unphone9.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -38403,7 +38953,7 @@ esp32c3m1IKit.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 esp32c3m1IKit.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) esp32c3m1IKit.menu.PartitionScheme.huge_app.build.partitions=huge_app esp32c3m1IKit.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -esp32c3m1IKit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +esp32c3m1IKit.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) esp32c3m1IKit.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs esp32c3m1IKit.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 esp32c3m1IKit.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -38513,7 +39063,7 @@ roboheart_hercules.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 roboheart_hercules.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) roboheart_hercules.menu.PartitionScheme.huge_app.build.partitions=huge_app roboheart_hercules.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -roboheart_hercules.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +roboheart_hercules.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) roboheart_hercules.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs roboheart_hercules.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 roboheart_hercules.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -38653,7 +39203,7 @@ VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.huge_app.build.partitions=huge_app VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 VALTRACK_V4_VTS_ESP32_C3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -38804,7 +39354,7 @@ VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.huge_app.build.partitions=huge_app VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 VALTRACK_V4_MFW_ESP32_C3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -39035,7 +39585,7 @@ Edgebox-ESP-100.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 Edgebox-ESP-100.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Edgebox-ESP-100.menu.PartitionScheme.huge_app.build.partitions=huge_app Edgebox-ESP-100.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Edgebox-ESP-100.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +Edgebox-ESP-100.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) Edgebox-ESP-100.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs Edgebox-ESP-100.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 Edgebox-ESP-100.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -39405,7 +39955,7 @@ nebulas3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 nebulas3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) nebulas3.menu.PartitionScheme.huge_app.build.partitions=huge_app nebulas3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -nebulas3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +nebulas3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) nebulas3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs nebulas3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 nebulas3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -39631,7 +40181,7 @@ lionbits3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 lionbits3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) lionbits3.menu.PartitionScheme.huge_app.build.partitions=huge_app lionbits3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -lionbits3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +lionbits3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) lionbits3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs lionbits3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 lionbits3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -39699,7 +40249,7 @@ lionbits3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## -gen4-ESP32-S3R8n16.name=4D Systems gen4-ESP32 16MB Modules (ESP32-S3R8n16) +gen4-ESP32-S3R8n16.name=4D Systems gen4-ESP32 Modules (ESP32-S3) gen4-ESP32-S3R8n16.bootloader.tool=esptool_py gen4-ESP32-S3R8n16.bootloader.tool.default=esptool_py @@ -39737,24 +40287,17 @@ gen4-ESP32-S3R8n16.build.flash_mode=dio gen4-ESP32-S3R8n16.build.boot=qio gen4-ESP32-S3R8n16.build.boot_freq=80m gen4-ESP32-S3R8n16.build.partitions=default -gen4-ESP32-S3R8n16.build.defines=-DBOARD_HAS_PSRAM +gen4-ESP32-S3R8n16.build.defines=-DBOARD_HAS_PSRAM -D{build.board} -D{build.DisplayModel} gen4-ESP32-S3R8n16.build.loop_core= gen4-ESP32-S3R8n16.build.event_core= gen4-ESP32-S3R8n16.build.psram_type=opi gen4-ESP32-S3R8n16.build.memory_type={build.boot}_{build.psram_type} -gen4-ESP32-S3R8n16.menu.PSRAM.opi=OPI PSRAM -gen4-ESP32-S3R8n16.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM -gen4-ESP32-S3R8n16.menu.PSRAM.opi.build.psram_type=opi - -gen4-ESP32-S3R8n16.menu.FlashMode.qio=QIO 80MHz -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.flash_mode=dio -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.boot=qio -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.boot_freq=80m -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.flash_freq=80m gen4-ESP32-S3R8n16.menu.FlashSize.16M=16MB (128Mb) gen4-ESP32-S3R8n16.menu.FlashSize.16M.build.flash_size=16MB +gen4-ESP32-S3R8n16.menu.FlashSize.32M=32MB (256Mb) +gen4-ESP32-S3R8n16.menu.FlashSize.32M.build.flash_size=32MB gen4-ESP32-S3R8n16.menu.LoopCore.1=Core 1 gen4-ESP32-S3R8n16.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 @@ -39805,6 +40348,15 @@ gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme3.upload.maximum_size=832 gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP) gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 gen4-ESP32-S3R8n16.menu.CPUFreq.240=240MHz (WiFi) gen4-ESP32-S3R8n16.menu.CPUFreq.240.build.f_cpu=240000000L @@ -39852,6 +40404,80 @@ gen4-ESP32-S3R8n16.menu.EraseFlash.none.upload.erase_cmd= gen4-ESP32-S3R8n16.menu.EraseFlash.all=Enabled gen4-ESP32-S3R8n16.menu.EraseFlash.all.upload.erase_cmd=-e +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24=gen4-ESP32-24 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24.build.DisplayModel=ESP32S3_24 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24t=gen4-ESP32-24T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24t.build.DisplayModel=ESP32S3_24T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct=gen4-ESP32-24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct.build.DisplayModel=ESP32S3_24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct_clb=gen4-ESP32-24CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct_clb.build.DisplayModel=ESP32S3_24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28=gen4-ESP32-28 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28.build.DisplayModel=ESP32S3_28 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28t=gen4-ESP32-28T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28t.build.DisplayModel=ESP32S3_28T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct=gen4-ESP32-28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct.build.DisplayModel=ESP32S3_28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct_clb=gen4-ESP32-28CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct_clb.build.DisplayModel=ESP32S3_28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32=gen4-ESP32-32 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32.build.DisplayModel=ESP32S3_32 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32t=gen4-ESP32-32T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32t.build.DisplayModel=ESP32S3_32T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct=gen4-ESP32-32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct.build.DisplayModel=ESP32S3_32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct_clb=gen4-ESP32-32CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct_clb.build.DisplayModel=ESP32S3_32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35=gen4-ESP32-35 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35.build.DisplayModel=ESP32S3_35 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35t=gen4-ESP32-35T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35t.build.DisplayModel=ESP32S3_35T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct=gen4-ESP32-35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct.build.DisplayModel=ESP32S3_35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct_clb=gen4-ESP32-35CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct_clb.build.DisplayModel=ESP32S3_35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43_qspi=gen4-ESP32Q-43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43_qspi.build.DisplayModel=ESP32S3_Q43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t_qspi=gen4-ESP32Q-43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t_qspi.build.DisplayModel=ESP32S3_Q43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_qspi=gen4-ESP32Q-43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_qspi.build.DisplayModel=ESP32S3_Q43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb_qspi=gen4-ESP32Q-43CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb_qspi.build.DisplayModel=ESP32S3_Q43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43=gen4-ESP32-43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43.build.DisplayModel=ESP32S3_43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t=gen4-ESP32-43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t.build.DisplayModel=ESP32S3_43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct=gen4-ESP32-43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct.build.DisplayModel=ESP32S3_43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb=gen4-ESP32-43CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb.build.DisplayModel=ESP32S3_43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50=gen4-ESP32-50 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50.build.DisplayModel=ESP32S3_50 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50t=gen4-ESP32-50T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50t.build.DisplayModel=ESP32S3_50T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct=gen4-ESP32-50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct.build.DisplayModel=ESP32S3_50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct_clb=gen4-ESP32-50CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct_clb.build.DisplayModel=ESP32S3_50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70=gen4-ESP32-70 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70.build.DisplayModel=ESP32S3_70 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70t=gen4-ESP32-70T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70t.build.DisplayModel=ESP32S3_70T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct=gen4-ESP32-70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct.build.DisplayModel=ESP32S3_70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct_clb=gen4-ESP32-70CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct_clb.build.DisplayModel=ESP32S3_70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90=ESP32-90 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90.build.DisplayModel=ESP32S3_90 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90t=ESP32-90T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90t.build.DisplayModel=ESP32S3_90T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct=ESP32-90CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct.build.DisplayModel=ESP32S3_90CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct_clb=ESP32-90CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct_clb.build.DisplayModel=ESP32S3_90CT + + ############################################################## # Namino Rosso @@ -39967,7 +40593,7 @@ namino_rosso.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 namino_rosso.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) namino_rosso.menu.PartitionScheme.huge_app.build.partitions=huge_app namino_rosso.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -namino_rosso.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +namino_rosso.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) namino_rosso.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs namino_rosso.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -40156,7 +40782,7 @@ namino_arancio.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 namino_arancio.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) namino_arancio.menu.PartitionScheme.huge_app.build.partitions=huge_app namino_arancio.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -namino_arancio.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +namino_arancio.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) namino_arancio.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs namino_arancio.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -40345,7 +40971,7 @@ namino_bianco.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 namino_bianco.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) namino_bianco.menu.PartitionScheme.huge_app.build.partitions=huge_app namino_bianco.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -namino_bianco.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +namino_bianco.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) namino_bianco.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs namino_bianco.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -40450,7 +41076,7 @@ ioxesp32.build.board=IOXESP32 ioxesp32.build.f_cpu=240000000L ioxesp32.build.flash_mode=dio ioxesp32.build.flash_size=4MB -ioxesp32ps.build.flash_freq=40m +ioxesp32.build.flash_freq=40m ioxesp32.build.boot=dio ioxesp32.build.partitions=default ioxesp32.build.defines= @@ -40460,9 +41086,6 @@ ioxesp32.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB S ioxesp32.menu.PartitionScheme.default.build.partitions=default ioxesp32.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) ioxesp32.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -ioxesp32.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -ioxesp32.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -ioxesp32.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 ioxesp32.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) ioxesp32.menu.PartitionScheme.minimal.build.partitions=minimal ioxesp32.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -40480,12 +41103,9 @@ ioxesp32.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ioxesp32.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ioxesp32.menu.PartitionScheme.huge_app.build.partitions=huge_app ioxesp32.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ioxesp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ioxesp32.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ioxesp32.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ioxesp32.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -ioxesp32.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -ioxesp32.menu.PartitionScheme.fatflash.build.partitions=ffat -ioxesp32.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 ioxesp32.menu.PartitionScheme.rainmaker=RainMaker 4MB ioxesp32.menu.PartitionScheme.rainmaker.build.partitions=rainmaker ioxesp32.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -40572,9 +41192,6 @@ ioxesp32ps.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB ioxesp32ps.menu.PartitionScheme.default.build.partitions=default ioxesp32ps.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) ioxesp32ps.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -ioxesp32ps.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -ioxesp32ps.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -ioxesp32ps.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 ioxesp32ps.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) ioxesp32ps.menu.PartitionScheme.minimal.build.partitions=minimal ioxesp32ps.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -40592,12 +41209,9 @@ ioxesp32ps.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ioxesp32ps.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ioxesp32ps.menu.PartitionScheme.huge_app.build.partitions=huge_app ioxesp32ps.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ioxesp32ps.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ioxesp32ps.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ioxesp32ps.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ioxesp32ps.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -ioxesp32ps.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -ioxesp32ps.menu.PartitionScheme.fatflash.build.partitions=ffat -ioxesp32ps.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 ioxesp32ps.menu.PartitionScheme.rainmaker=RainMaker 4MB ioxesp32ps.menu.PartitionScheme.rainmaker.build.partitions=rainmaker ioxesp32ps.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 @@ -40724,7 +41338,7 @@ ioxesp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ioxesp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ioxesp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app ioxesp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ioxesp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ioxesp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ioxesp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ioxesp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 ioxesp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -40741,7 +41355,7 @@ ioxesp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr ioxesp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 ioxesp32c6.menu.PartitionScheme.custom=Custom ioxesp32c6.menu.PartitionScheme.custom.build.partitions= -ioxesp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +ioxesp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 ioxesp32c6.menu.CPUFreq.160=160MHz (WiFi) ioxesp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -40935,7 +41549,7 @@ atd147_s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 atd147_s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) atd147_s3.menu.PartitionScheme.huge_app.build.partitions=huge_app atd147_s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -atd147_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +atd147_s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) atd147_s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs atd147_s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 atd147_s3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -41118,7 +41732,7 @@ atd35s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 atd35s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) atd35s3.menu.PartitionScheme.huge_app.build.partitions=huge_app atd35s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -atd35s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +atd35s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) atd35s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs atd35s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 atd35s3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -41449,7 +42063,7 @@ sensebox_mcu_esp32s2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10485 sensebox_mcu_esp32s2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) sensebox_mcu_esp32s2.menu.PartitionScheme.huge_app.build.partitions=huge_app sensebox_mcu_esp32s2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -sensebox_mcu_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +sensebox_mcu_esp32s2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) sensebox_mcu_esp32s2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs sensebox_mcu_esp32s2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -41542,11 +42156,11 @@ sensebox_eye.upload.maximum_size=1310720 sensebox_eye.upload.maximum_data_size=327680 sensebox_eye.upload.flags= sensebox_eye.upload.extra_flags= -sensebox_eye.upload.use_1200bps_touch=true -sensebox_eye.upload.wait_for_upload_port=true +sensebox_eye.upload.use_1200bps_touch=false +sensebox_eye.upload.wait_for_upload_port=false -sensebox_eye.serial.disableDTR=true -sensebox_eye.serial.disableRTS=true +sensebox_eye.serial.disableDTR=false +sensebox_eye.serial.disableRTS=false sensebox_eye.build.tarch=xtensa sensebox_eye.build.bootloader_addr=0x0 @@ -41566,7 +42180,7 @@ sensebox_eye.build.flash_freq=80m sensebox_eye.build.flash_mode=dio sensebox_eye.build.boot=qio sensebox_eye.build.boot_freq=80m -sensebox_eye.build.partitions=default_16MB +sensebox_eye.build.partitions=tinyuf2 sensebox_eye.build.defines= sensebox_eye.build.loop_core= sensebox_eye.build.event_core= @@ -41585,12 +42199,12 @@ sensebox_eye.menu.JTAGAdapter.bridge=ESP USB Bridge sensebox_eye.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg sensebox_eye.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 -sensebox_eye.menu.PSRAM.disabled=Disabled -sensebox_eye.menu.PSRAM.disabled.build.defines= -sensebox_eye.menu.PSRAM.disabled.build.psram_type=qspi sensebox_eye.menu.PSRAM.opi=OPI PSRAM sensebox_eye.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM sensebox_eye.menu.PSRAM.opi.build.psram_type=opi +sensebox_eye.menu.PSRAM.disabled=Disabled +sensebox_eye.menu.PSRAM.disabled.build.defines= +sensebox_eye.menu.PSRAM.disabled.build.psram_type=qspi sensebox_eye.menu.FlashMode.qio=QIO 80MHz sensebox_eye.menu.FlashMode.qio.build.flash_mode=dio @@ -41616,33 +42230,38 @@ sensebox_eye.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 sensebox_eye.menu.EventsCore.0=Core 0 sensebox_eye.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 -sensebox_eye.menu.USBMode.hwcdc=Hardware CDC and JTAG -sensebox_eye.menu.USBMode.hwcdc.build.usb_mode=1 sensebox_eye.menu.USBMode.default=USB-OTG (TinyUSB) sensebox_eye.menu.USBMode.default.build.usb_mode=0 +sensebox_eye.menu.USBMode.hwcdc=Hardware CDC and JTAG +sensebox_eye.menu.USBMode.hwcdc.build.usb_mode=1 sensebox_eye.menu.CDCOnBoot.default=Enabled sensebox_eye.menu.CDCOnBoot.default.build.cdc_on_boot=1 sensebox_eye.menu.CDCOnBoot.cdc=Disabled sensebox_eye.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 -sensebox_eye.menu.MSCOnBoot.default=Disabled -sensebox_eye.menu.MSCOnBoot.default.build.msc_on_boot=0 -sensebox_eye.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) -sensebox_eye.menu.MSCOnBoot.msc.build.msc_on_boot=1 +sensebox_eye.menu.MSCOnBoot.default=Enabled (Requires USB-OTG Mode) +sensebox_eye.menu.MSCOnBoot.default.build.msc_on_boot=1 +sensebox_eye.menu.MSCOnBoot.msc=Disabled +sensebox_eye.menu.MSCOnBoot.msc.build.msc_on_boot=0 sensebox_eye.menu.DFUOnBoot.default=Disabled sensebox_eye.menu.DFUOnBoot.default.build.dfu_on_boot=0 sensebox_eye.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) sensebox_eye.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 -sensebox_eye.menu.UploadMode.default=UART0 / Hardware CDC -sensebox_eye.menu.UploadMode.default.upload.use_1200bps_touch=false -sensebox_eye.menu.UploadMode.default.upload.wait_for_upload_port=false sensebox_eye.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) sensebox_eye.menu.UploadMode.cdc.upload.use_1200bps_touch=true sensebox_eye.menu.UploadMode.cdc.upload.wait_for_upload_port=true +sensebox_eye.menu.UploadMode.default=UART0 / Hardware CDC +sensebox_eye.menu.UploadMode.default.upload.use_1200bps_touch=false +sensebox_eye.menu.UploadMode.default.upload.wait_for_upload_port=false +sensebox_eye.menu.PartitionScheme.tinyuf2=TinyUF2 Compatibility (2MB APP/12MB FFAT) +sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader_tinyuf2 +sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-16MB-tinyuf2 +sensebox_eye.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 +sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" 0x210000 "{runtime.platform.path}/variants/{build.variant}/APOTA.bin" sensebox_eye.menu.PartitionScheme.default_16MB=Default (6.25MB APP/3.43MB SPIFFS) sensebox_eye.menu.PartitionScheme.default_16MB.build.partitions=default_16MB sensebox_eye.menu.PartitionScheme.default_16MB.upload.maximum_size=6553600 @@ -41655,11 +42274,6 @@ sensebox_eye.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 sensebox_eye.menu.PartitionScheme.fatflash=Large FFAT (2MB APP/12.5MB FATFS) sensebox_eye.menu.PartitionScheme.fatflash.build.partitions=ffat sensebox_eye.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -sensebox_eye.menu.PartitionScheme.tinyuf2=TinyUF2 Compatibility (2MB APP/12MB FFAT) -sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader_tinyuf2 -sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions_tinyuf2 -sensebox_eye.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 -sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" sensebox_eye.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP) sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144 @@ -41727,6 +42341,8 @@ nano_nora.upload.tool.network=esp_ota nano_nora.upload.protocol=serial nano_nora.upload.maximum_size=3145728 nano_nora.upload.maximum_data_size=327680 +nano_nora.upload.flags= +nano_nora.upload.extra_flags= nano_nora.upload.use_1200bps_touch=false nano_nora.upload.wait_for_upload_port=false @@ -41770,8 +42386,8 @@ nano_nora.debug_config.nano_nora.cortex-debug.custom.overrideRestartCommands.1=m nano_nora.debug_config.nano_nora.cortex-debug.custom.overrideRestartCommands.2=interrupt nano_nora.debug.additional_config=debug_config.nano_nora -nano_nora.tools.esptool_py.program.pattern_args=--chip {build.mcu} --port "{serial.port}" --before default_reset --after hard_reset write_flash -z --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0xf70000 "{build.variant.path}/extra/nora_recovery/nora_recovery.ino.bin" 0x10000 "{build.path}/{build.project_name}.bin" -nano_nora.tools.esptool_py.erase.pattern_args=--chip {build.mcu} --port "{serial.port}" --before default_reset --after hard_reset erase_flash +nano_nora.tools.esptool_py.program.pattern_args=--chip {build.mcu} --port "{serial.port}" --before default-reset --after hard-reset write-flash -z --flash-mode {build.flash_mode} --flash-freq {build.flash_freq} --flash-size {build.flash_size} {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0xf70000 "{build.variant.path}/extra/nora_recovery/nora_recovery.ino.bin" 0x10000 "{build.path}/{build.project_name}.bin" +nano_nora.tools.esptool_py.erase.pattern_args=--chip {build.mcu} --port "{serial.port}" --before default-reset --after hard-reset erase-flash nano_nora.debug.executable= @@ -41788,6 +42404,19 @@ nano_nora.menu.USBMode.hwcdc=Debug mode (Hardware CDC) nano_nora.menu.USBMode.hwcdc.build.usb_mode=1 nano_nora.menu.USBMode.hwcdc.debug.executable={build.path}/{build.project_name}.elf +nano_nora.menu.DebugLevel.none=None +nano_nora.menu.DebugLevel.none.build.code_debug=0 +nano_nora.menu.DebugLevel.error=Error +nano_nora.menu.DebugLevel.error.build.code_debug=1 +nano_nora.menu.DebugLevel.warn=Warn +nano_nora.menu.DebugLevel.warn.build.code_debug=2 +nano_nora.menu.DebugLevel.info=Info +nano_nora.menu.DebugLevel.info.build.code_debug=3 +nano_nora.menu.DebugLevel.debug=Debug +nano_nora.menu.DebugLevel.debug.build.code_debug=4 +nano_nora.menu.DebugLevel.verbose=Verbose +nano_nora.menu.DebugLevel.verbose.build.code_debug=5 + ############################################################## makergo_c3_supermini.name=MakerGO ESP32 C3 SuperMini @@ -41902,6 +42531,171 @@ makergo_c3_supermini.menu.EraseFlash.none.upload.erase_cmd= makergo_c3_supermini.menu.EraseFlash.all=Enabled makergo_c3_supermini.menu.EraseFlash.all.upload.erase_cmd=-e +############################################################## + +makergo_c6_supermini.name=MakerGO ESP32 C6 SuperMini + +makergo_c6_supermini.bootloader.tool=esptool_py +makergo_c6_supermini.bootloader.tool.default=esptool_py + +makergo_c6_supermini.upload.tool=esptool_py +makergo_c6_supermini.upload.tool.default=esptool_py +makergo_c6_supermini.upload.tool.network=esp_ota + +makergo_c6_supermini.upload.maximum_size=1310720 +makergo_c6_supermini.upload.maximum_data_size=327680 +makergo_c6_supermini.upload.flags= +makergo_c6_supermini.upload.extra_flags= +makergo_c6_supermini.upload.use_1200bps_touch=false +makergo_c6_supermini.upload.wait_for_upload_port=false + +makergo_c6_supermini.serial.disableDTR=false +makergo_c6_supermini.serial.disableRTS=false + +makergo_c6_supermini.build.tarch=riscv32 +makergo_c6_supermini.build.target=esp +makergo_c6_supermini.build.mcu=esp32c6 +makergo_c6_supermini.build.core=esp32 +makergo_c6_supermini.build.variant=makergo_c6_supermini +makergo_c6_supermini.build.board=MAKERGO_C6_SUPERMINI +makergo_c6_supermini.build.bootloader_addr=0x0 + +makergo_c6_supermini.build.cdc_on_boot=0 +makergo_c6_supermini.build.f_cpu=160000000L +makergo_c6_supermini.build.flash_size=4MB +makergo_c6_supermini.build.flash_freq=80m +makergo_c6_supermini.build.flash_mode=qio +makergo_c6_supermini.build.boot=qio +makergo_c6_supermini.build.partitions=default +makergo_c6_supermini.build.defines= + +## IDE 2.0 Seems to not update the value +makergo_c6_supermini.menu.JTAGAdapter.default=Disabled +makergo_c6_supermini.menu.JTAGAdapter.default.build.copy_jtag_files=0 +makergo_c6_supermini.menu.JTAGAdapter.builtin=Integrated USB JTAG +makergo_c6_supermini.menu.JTAGAdapter.builtin.build.openocdscript=esp32c6-builtin.cfg +makergo_c6_supermini.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +makergo_c6_supermini.menu.JTAGAdapter.external=FTDI Adapter +makergo_c6_supermini.menu.JTAGAdapter.external.build.openocdscript=esp32c6-ftdi.cfg +makergo_c6_supermini.menu.JTAGAdapter.external.build.copy_jtag_files=1 +makergo_c6_supermini.menu.JTAGAdapter.bridge=ESP USB Bridge +makergo_c6_supermini.menu.JTAGAdapter.bridge.build.openocdscript=esp32c6-bridge.cfg +makergo_c6_supermini.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +makergo_c6_supermini.menu.CDCOnBoot.default=Disabled +makergo_c6_supermini.menu.CDCOnBoot.default.build.cdc_on_boot=0 +makergo_c6_supermini.menu.CDCOnBoot.cdc=Enabled +makergo_c6_supermini.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +makergo_c6_supermini.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.default.build.partitions=default +makergo_c6_supermini.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +makergo_c6_supermini.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +makergo_c6_supermini.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.minimal.build.partitions=minimal +makergo_c6_supermini.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.no_ota.build.partitions=no_ota +makergo_c6_supermini.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +makergo_c6_supermini.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +makergo_c6_supermini.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +makergo_c6_supermini.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +makergo_c6_supermini.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +makergo_c6_supermini.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +makergo_c6_supermini.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +makergo_c6_supermini.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +makergo_c6_supermini.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +makergo_c6_supermini.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.huge_app.build.partitions=huge_app +makergo_c6_supermini.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +makergo_c6_supermini.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +makergo_c6_supermini.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +makergo_c6_supermini.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +makergo_c6_supermini.menu.PartitionScheme.rainmaker=RainMaker 4MB +makergo_c6_supermini.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +makergo_c6_supermini.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +makergo_c6_supermini.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +makergo_c6_supermini.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +makergo_c6_supermini.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +makergo_c6_supermini.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +makergo_c6_supermini.menu.PartitionScheme.zigbee.build.partitions=zigbee +makergo_c6_supermini.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +makergo_c6_supermini.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +makergo_c6_supermini.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +makergo_c6_supermini.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +makergo_c6_supermini.menu.PartitionScheme.custom=Custom +makergo_c6_supermini.menu.PartitionScheme.custom.build.partitions= +makergo_c6_supermini.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +makergo_c6_supermini.menu.CPUFreq.160=160MHz (WiFi) +makergo_c6_supermini.menu.CPUFreq.160.build.f_cpu=160000000L +makergo_c6_supermini.menu.CPUFreq.80=80MHz (WiFi) +makergo_c6_supermini.menu.CPUFreq.80.build.f_cpu=80000000L +makergo_c6_supermini.menu.CPUFreq.40=40MHz +makergo_c6_supermini.menu.CPUFreq.40.build.f_cpu=40000000L +makergo_c6_supermini.menu.CPUFreq.20=20MHz +makergo_c6_supermini.menu.CPUFreq.20.build.f_cpu=20000000L +makergo_c6_supermini.menu.CPUFreq.10=10MHz +makergo_c6_supermini.menu.CPUFreq.10.build.f_cpu=10000000L + +makergo_c6_supermini.menu.FlashMode.qio=QIO +makergo_c6_supermini.menu.FlashMode.qio.build.flash_mode=dio +makergo_c6_supermini.menu.FlashMode.qio.build.boot=qio +makergo_c6_supermini.menu.FlashMode.dio=DIO +makergo_c6_supermini.menu.FlashMode.dio.build.flash_mode=dio +makergo_c6_supermini.menu.FlashMode.dio.build.boot=dio + +makergo_c6_supermini.menu.FlashFreq.80=80MHz +makergo_c6_supermini.menu.FlashFreq.80.build.flash_freq=80m +makergo_c6_supermini.menu.FlashFreq.40=40MHz +makergo_c6_supermini.menu.FlashFreq.40.build.flash_freq=40m + +makergo_c6_supermini.menu.FlashSize.4M=4MB (32Mb) +makergo_c6_supermini.menu.FlashSize.4M.build.flash_size=4MB + +makergo_c6_supermini.menu.UploadSpeed.921600=921600 +makergo_c6_supermini.menu.UploadSpeed.921600.upload.speed=921600 +makergo_c6_supermini.menu.UploadSpeed.115200=115200 +makergo_c6_supermini.menu.UploadSpeed.115200.upload.speed=115200 +makergo_c6_supermini.menu.UploadSpeed.256000.windows=256000 +makergo_c6_supermini.menu.UploadSpeed.256000.upload.speed=256000 +makergo_c6_supermini.menu.UploadSpeed.230400.windows.upload.speed=256000 +makergo_c6_supermini.menu.UploadSpeed.230400=230400 +makergo_c6_supermini.menu.UploadSpeed.230400.upload.speed=230400 +makergo_c6_supermini.menu.UploadSpeed.460800.linux=460800 +makergo_c6_supermini.menu.UploadSpeed.460800.macosx=460800 +makergo_c6_supermini.menu.UploadSpeed.460800.upload.speed=460800 +makergo_c6_supermini.menu.UploadSpeed.512000.windows=512000 +makergo_c6_supermini.menu.UploadSpeed.512000.upload.speed=512000 + +makergo_c6_supermini.menu.DebugLevel.none=None +makergo_c6_supermini.menu.DebugLevel.none.build.code_debug=0 +makergo_c6_supermini.menu.DebugLevel.error=Error +makergo_c6_supermini.menu.DebugLevel.error.build.code_debug=1 +makergo_c6_supermini.menu.DebugLevel.warn=Warn +makergo_c6_supermini.menu.DebugLevel.warn.build.code_debug=2 +makergo_c6_supermini.menu.DebugLevel.info=Info +makergo_c6_supermini.menu.DebugLevel.info.build.code_debug=3 +makergo_c6_supermini.menu.DebugLevel.debug=Debug +makergo_c6_supermini.menu.DebugLevel.debug.build.code_debug=4 +makergo_c6_supermini.menu.DebugLevel.verbose=Verbose +makergo_c6_supermini.menu.DebugLevel.verbose.build.code_debug=5 + +makergo_c6_supermini.menu.EraseFlash.none=Disabled +makergo_c6_supermini.menu.EraseFlash.none.upload.erase_cmd= +makergo_c6_supermini.menu.EraseFlash.all=Enabled +makergo_c6_supermini.menu.EraseFlash.all.upload.erase_cmd=-e + +makergo_c6_supermini.menu.ZigbeeMode.default=Disabled +makergo_c6_supermini.menu.ZigbeeMode.default.build.zigbee_mode= +makergo_c6_supermini.menu.ZigbeeMode.default.build.zigbee_libs= +makergo_c6_supermini.menu.ZigbeeMode.ed=Zigbee ED (end device) +makergo_c6_supermini.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +makergo_c6_supermini.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +makergo_c6_supermini.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +makergo_c6_supermini.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +makergo_c6_supermini.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native + ############################################################## # ThingPulse ePulse Feather @@ -42090,7 +42884,7 @@ epulse_feather_c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 epulse_feather_c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) epulse_feather_c6.menu.PartitionScheme.huge_app.build.partitions=huge_app epulse_feather_c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -epulse_feather_c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +epulse_feather_c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) epulse_feather_c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs epulse_feather_c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 epulse_feather_c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -42107,7 +42901,7 @@ epulse_feather_c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr epulse_feather_c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 epulse_feather_c6.menu.PartitionScheme.custom=Custom epulse_feather_c6.menu.PartitionScheme.custom.build.partitions= -epulse_feather_c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +epulse_feather_c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 epulse_feather_c6.menu.CPUFreq.160=160MHz (WiFi) epulse_feather_c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -42352,7 +43146,7 @@ Geekble_Nano_ESP32S3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10485 Geekble_Nano_ESP32S3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Geekble_Nano_ESP32S3.menu.PartitionScheme.huge_app.build.partitions=huge_app Geekble_Nano_ESP32S3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Geekble_Nano_ESP32S3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +Geekble_Nano_ESP32S3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) Geekble_Nano_ESP32S3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs Geekble_Nano_ESP32S3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 Geekble_Nano_ESP32S3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -42369,7 +43163,7 @@ Geekble_Nano_ESP32S3.menu.PartitionScheme.all_app.build.custom_partitions=max_ap Geekble_Nano_ESP32S3.menu.PartitionScheme.all_app.upload.maximum_size=4063232 Geekble_Nano_ESP32S3.menu.PartitionScheme.custom=Custom Geekble_Nano_ESP32S3.menu.PartitionScheme.custom.build.partitions= -Geekble_Nano_ESP32S3.menu.PartitionScheme.custom.upload.maximum_size=16777216 +Geekble_Nano_ESP32S3.menu.PartitionScheme.custom.upload.maximum_size=4194304 Geekble_Nano_ESP32S3.menu.PSRAM.disabled=Disabled Geekble_Nano_ESP32S3.menu.PSRAM.disabled.build.defines= @@ -42480,10 +43274,10 @@ waveshare_esp32_s3_zero.menu.USBMode.hwcdc.build.usb_mode=1 waveshare_esp32_s3_zero.menu.USBMode.default=USB-OTG (TinyUSB) waveshare_esp32_s3_zero.menu.USBMode.default.build.usb_mode=0 -waveshare_esp32_s3_zero.menu.CDCOnBoot.default=Disabled -waveshare_esp32_s3_zero.menu.CDCOnBoot.default.build.cdc_on_boot=0 -waveshare_esp32_s3_zero.menu.CDCOnBoot.cdc=Enabled -waveshare_esp32_s3_zero.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +waveshare_esp32_s3_zero.menu.CDCOnBoot.default=Enabled +waveshare_esp32_s3_zero.menu.CDCOnBoot.default.build.cdc_on_boot=1 +waveshare_esp32_s3_zero.menu.CDCOnBoot.cdc=Disabled +waveshare_esp32_s3_zero.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 waveshare_esp32_s3_zero.menu.MSCOnBoot.default=Disabled waveshare_esp32_s3_zero.menu.MSCOnBoot.default.build.msc_on_boot=0 @@ -42521,7 +43315,7 @@ waveshare_esp32_s3_zero.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10 waveshare_esp32_s3_zero.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_zero.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_zero.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_zero.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_zero.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_zero.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_zero.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_zero.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -42530,17 +43324,15 @@ waveshare_esp32_s3_zero.menu.PartitionScheme.rainmaker.upload.maximum_size=19660 waveshare_esp32_s3_zero.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA waveshare_esp32_s3_zero.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota waveshare_esp32_s3_zero.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 - waveshare_esp32_s3_zero.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_zero.menu.PartitionScheme.otanofs.build.custom_partitions=ota_nofs_4MB waveshare_esp32_s3_zero.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_zero.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_zero.menu.PartitionScheme.all_app.build.custom_partitions=max_app_4MB waveshare_esp32_s3_zero.menu.PartitionScheme.all_app.upload.maximum_size=4063232 - waveshare_esp32_s3_zero.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_zero.menu.PartitionScheme.custom.build.partitions= -waveshare_esp32_s3_zero.menu.PartitionScheme.custom.upload.maximum_size=16777216 +waveshare_esp32_s3_zero.menu.PartitionScheme.custom.upload.maximum_size=4194304 waveshare_esp32_s3_zero.menu.CPUFreq.240=240MHz (WiFi) waveshare_esp32_s3_zero.menu.CPUFreq.240.build.f_cpu=240000000L @@ -42716,7 +43508,7 @@ ws_esp32_s3_matrix.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 ws_esp32_s3_matrix.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) ws_esp32_s3_matrix.menu.PartitionScheme.huge_app.build.partitions=huge_app ws_esp32_s3_matrix.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -ws_esp32_s3_matrix.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +ws_esp32_s3_matrix.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) ws_esp32_s3_matrix.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs ws_esp32_s3_matrix.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 ws_esp32_s3_matrix.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -42725,17 +43517,15 @@ ws_esp32_s3_matrix.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 ws_esp32_s3_matrix.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA ws_esp32_s3_matrix.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota ws_esp32_s3_matrix.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 - ws_esp32_s3_matrix.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) ws_esp32_s3_matrix.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB ws_esp32_s3_matrix.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 ws_esp32_s3_matrix.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) ws_esp32_s3_matrix.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB ws_esp32_s3_matrix.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - ws_esp32_s3_matrix.menu.PartitionScheme.custom=Custom ws_esp32_s3_matrix.menu.PartitionScheme.custom.build.partitions= -ws_esp32_s3_matrix.menu.PartitionScheme.custom.upload.maximum_size=16777216 +ws_esp32_s3_matrix.menu.PartitionScheme.custom.upload.maximum_size=4194304 ws_esp32_s3_matrix.menu.CPUFreq.240=240MHz (WiFi) ws_esp32_s3_matrix.menu.CPUFreq.240.build.f_cpu=240000000L @@ -42890,6 +43680,8 @@ waveshare_esp32_s3_touch_lcd_169.menu.UploadMode.cdc.upload.use_1200bps_touch=tr waveshare_esp32_s3_touch_lcd_169.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -42909,7 +43701,7 @@ waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.noota_3gffat.upload.maximu waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -42921,17 +43713,12 @@ waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.rainmaker_8MB.upload.maxim waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_169.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -43089,6 +43876,8 @@ waveshare_esp32_s3_touch_amoled_18.menu.UploadMode.cdc.upload.use_1200bps_touch= waveshare_esp32_s3_touch_amoled_18.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -43108,7 +43897,7 @@ waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.noota_3gffat.upload.maxi waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -43120,17 +43909,12 @@ waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.rainmaker_8MB.upload.max waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_amoled_18.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -43288,6 +44072,8 @@ waveshare_esp32_s3_lcd_169.menu.UploadMode.cdc.upload.use_1200bps_touch=true waveshare_esp32_s3_lcd_169.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_lcd_169.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_lcd_169.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -43307,7 +44093,7 @@ waveshare_esp32_s3_lcd_169.menu.PartitionScheme.noota_3gffat.upload.maximum_size waveshare_esp32_s3_lcd_169.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_lcd_169.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_lcd_169.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_lcd_169.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_lcd_169.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_lcd_169.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -43319,17 +44105,12 @@ waveshare_esp32_s3_lcd_169.menu.PartitionScheme.rainmaker_8MB.upload.maximum_siz waveshare_esp32_s3_lcd_169.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_lcd_169.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_lcd_169.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_lcd_169.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_lcd_169.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_lcd_169.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_lcd_169.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_lcd_169.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_lcd_169.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_lcd_169.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_lcd_169.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -43457,7 +44238,7 @@ waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.noota_3gffat.upload.maximum waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32s3_touch_lcd_128.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FAT) @@ -43534,6 +44315,171 @@ waveshare_esp32s3_touch_lcd_128.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +waveshare_esp32_c6_zero.name=Waveshare ESP32-C6 Zero + +waveshare_esp32_c6_zero.bootloader.tool=esptool_py +waveshare_esp32_c6_zero.bootloader.tool.default=esptool_py + +waveshare_esp32_c6_zero.upload.tool=esptool_py +waveshare_esp32_c6_zero.upload.tool.default=esptool_py +waveshare_esp32_c6_zero.upload.tool.network=esp_ota + +waveshare_esp32_c6_zero.upload.maximum_size=1310720 +waveshare_esp32_c6_zero.upload.maximum_data_size=327680 +waveshare_esp32_c6_zero.upload.flags= +waveshare_esp32_c6_zero.upload.extra_flags= +waveshare_esp32_c6_zero.upload.use_1200bps_touch=false +waveshare_esp32_c6_zero.upload.wait_for_upload_port=false + +waveshare_esp32_c6_zero.serial.disableDTR=false +waveshare_esp32_c6_zero.serial.disableRTS=false + +waveshare_esp32_c6_zero.build.tarch=riscv32 +waveshare_esp32_c6_zero.build.target=esp +waveshare_esp32_c6_zero.build.mcu=esp32c6 +waveshare_esp32_c6_zero.build.core=esp32 +waveshare_esp32_c6_zero.build.variant=waveshare_esp32_c6_zero +waveshare_esp32_c6_zero.build.board=WAVESHARE_ESP32_C6_ZERO +waveshare_esp32_c6_zero.build.bootloader_addr=0x0 + +waveshare_esp32_c6_zero.build.cdc_on_boot=0 +waveshare_esp32_c6_zero.build.f_cpu=160000000L +waveshare_esp32_c6_zero.build.flash_size=4MB +waveshare_esp32_c6_zero.build.flash_freq=80m +waveshare_esp32_c6_zero.build.flash_mode=qio +waveshare_esp32_c6_zero.build.boot=qio +waveshare_esp32_c6_zero.build.partitions=default +waveshare_esp32_c6_zero.build.defines= + +## IDE 2.0 Seems to not update the value +waveshare_esp32_c6_zero.menu.JTAGAdapter.default=Disabled +waveshare_esp32_c6_zero.menu.JTAGAdapter.default.build.copy_jtag_files=0 +waveshare_esp32_c6_zero.menu.JTAGAdapter.builtin=Integrated USB JTAG +waveshare_esp32_c6_zero.menu.JTAGAdapter.builtin.build.openocdscript=esp32c6-builtin.cfg +waveshare_esp32_c6_zero.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +waveshare_esp32_c6_zero.menu.JTAGAdapter.external=FTDI Adapter +waveshare_esp32_c6_zero.menu.JTAGAdapter.external.build.openocdscript=esp32c6-ftdi.cfg +waveshare_esp32_c6_zero.menu.JTAGAdapter.external.build.copy_jtag_files=1 +waveshare_esp32_c6_zero.menu.JTAGAdapter.bridge=ESP USB Bridge +waveshare_esp32_c6_zero.menu.JTAGAdapter.bridge.build.openocdscript=esp32c6-bridge.cfg +waveshare_esp32_c6_zero.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +waveshare_esp32_c6_zero.menu.CDCOnBoot.default=Disabled +waveshare_esp32_c6_zero.menu.CDCOnBoot.default.build.cdc_on_boot=0 +waveshare_esp32_c6_zero.menu.CDCOnBoot.cdc=Enabled +waveshare_esp32_c6_zero.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +waveshare_esp32_c6_zero.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.default.build.partitions=default +waveshare_esp32_c6_zero.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +waveshare_esp32_c6_zero.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.minimal.build.partitions=minimal +waveshare_esp32_c6_zero.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.no_ota.build.partitions=no_ota +waveshare_esp32_c6_zero.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +waveshare_esp32_c6_zero.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +waveshare_esp32_c6_zero.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.huge_app.build.partitions=huge_app +waveshare_esp32_c6_zero.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +waveshare_esp32_c6_zero.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_c6_zero.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +waveshare_esp32_c6_zero.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker=RainMaker 4MB +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +waveshare_esp32_c6_zero.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee.build.partitions=zigbee +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +waveshare_esp32_c6_zero.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +waveshare_esp32_c6_zero.menu.PartitionScheme.custom=Custom +waveshare_esp32_c6_zero.menu.PartitionScheme.custom.build.partitions= +waveshare_esp32_c6_zero.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +waveshare_esp32_c6_zero.menu.CPUFreq.160=160MHz (WiFi) +waveshare_esp32_c6_zero.menu.CPUFreq.160.build.f_cpu=160000000L +waveshare_esp32_c6_zero.menu.CPUFreq.80=80MHz (WiFi) +waveshare_esp32_c6_zero.menu.CPUFreq.80.build.f_cpu=80000000L +waveshare_esp32_c6_zero.menu.CPUFreq.40=40MHz +waveshare_esp32_c6_zero.menu.CPUFreq.40.build.f_cpu=40000000L +waveshare_esp32_c6_zero.menu.CPUFreq.20=20MHz +waveshare_esp32_c6_zero.menu.CPUFreq.20.build.f_cpu=20000000L +waveshare_esp32_c6_zero.menu.CPUFreq.10=10MHz +waveshare_esp32_c6_zero.menu.CPUFreq.10.build.f_cpu=10000000L + +waveshare_esp32_c6_zero.menu.FlashMode.qio=QIO +waveshare_esp32_c6_zero.menu.FlashMode.qio.build.flash_mode=dio +waveshare_esp32_c6_zero.menu.FlashMode.qio.build.boot=qio +waveshare_esp32_c6_zero.menu.FlashMode.dio=DIO +waveshare_esp32_c6_zero.menu.FlashMode.dio.build.flash_mode=dio +waveshare_esp32_c6_zero.menu.FlashMode.dio.build.boot=dio + +waveshare_esp32_c6_zero.menu.FlashFreq.80=80MHz +waveshare_esp32_c6_zero.menu.FlashFreq.80.build.flash_freq=80m +waveshare_esp32_c6_zero.menu.FlashFreq.40=40MHz +waveshare_esp32_c6_zero.menu.FlashFreq.40.build.flash_freq=40m + +waveshare_esp32_c6_zero.menu.FlashSize.4M=4MB (32Mb) +waveshare_esp32_c6_zero.menu.FlashSize.4M.build.flash_size=4MB + +waveshare_esp32_c6_zero.menu.UploadSpeed.921600=921600 +waveshare_esp32_c6_zero.menu.UploadSpeed.921600.upload.speed=921600 +waveshare_esp32_c6_zero.menu.UploadSpeed.115200=115200 +waveshare_esp32_c6_zero.menu.UploadSpeed.115200.upload.speed=115200 +waveshare_esp32_c6_zero.menu.UploadSpeed.256000.windows=256000 +waveshare_esp32_c6_zero.menu.UploadSpeed.256000.upload.speed=256000 +waveshare_esp32_c6_zero.menu.UploadSpeed.230400.windows.upload.speed=256000 +waveshare_esp32_c6_zero.menu.UploadSpeed.230400=230400 +waveshare_esp32_c6_zero.menu.UploadSpeed.230400.upload.speed=230400 +waveshare_esp32_c6_zero.menu.UploadSpeed.460800.linux=460800 +waveshare_esp32_c6_zero.menu.UploadSpeed.460800.macosx=460800 +waveshare_esp32_c6_zero.menu.UploadSpeed.460800.upload.speed=460800 +waveshare_esp32_c6_zero.menu.UploadSpeed.512000.windows=512000 +waveshare_esp32_c6_zero.menu.UploadSpeed.512000.upload.speed=512000 + +waveshare_esp32_c6_zero.menu.DebugLevel.none=None +waveshare_esp32_c6_zero.menu.DebugLevel.none.build.code_debug=0 +waveshare_esp32_c6_zero.menu.DebugLevel.error=Error +waveshare_esp32_c6_zero.menu.DebugLevel.error.build.code_debug=1 +waveshare_esp32_c6_zero.menu.DebugLevel.warn=Warn +waveshare_esp32_c6_zero.menu.DebugLevel.warn.build.code_debug=2 +waveshare_esp32_c6_zero.menu.DebugLevel.info=Info +waveshare_esp32_c6_zero.menu.DebugLevel.info.build.code_debug=3 +waveshare_esp32_c6_zero.menu.DebugLevel.debug=Debug +waveshare_esp32_c6_zero.menu.DebugLevel.debug.build.code_debug=4 +waveshare_esp32_c6_zero.menu.DebugLevel.verbose=Verbose +waveshare_esp32_c6_zero.menu.DebugLevel.verbose.build.code_debug=5 + +waveshare_esp32_c6_zero.menu.EraseFlash.none=Disabled +waveshare_esp32_c6_zero.menu.EraseFlash.none.upload.erase_cmd= +waveshare_esp32_c6_zero.menu.EraseFlash.all=Enabled +waveshare_esp32_c6_zero.menu.EraseFlash.all.upload.erase_cmd=-e + +waveshare_esp32_c6_zero.menu.ZigbeeMode.default=Disabled +waveshare_esp32_c6_zero.menu.ZigbeeMode.default.build.zigbee_mode= +waveshare_esp32_c6_zero.menu.ZigbeeMode.default.build.zigbee_libs= +waveshare_esp32_c6_zero.menu.ZigbeeMode.ed=Zigbee ED (end device) +waveshare_esp32_c6_zero.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +waveshare_esp32_c6_zero.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +waveshare_esp32_c6_zero.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +waveshare_esp32_c6_zero.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +waveshare_esp32_c6_zero.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native + +############################################################## + weact_studio_esp32c3.name=WeAct Studio ESP32C3 weact_studio_esp32c3.upload.tool=esptool_py @@ -43593,9 +44539,6 @@ weact_studio_esp32c3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB weact_studio_esp32c3.menu.PartitionScheme.default.build.partitions=default weact_studio_esp32c3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) weact_studio_esp32c3.menu.PartitionScheme.defaultffat.build.partitions=default_ffat -weact_studio_esp32c3.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) -weact_studio_esp32c3.menu.PartitionScheme.default_8MB.build.partitions=default_8MB -weact_studio_esp32c3.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 weact_studio_esp32c3.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) weact_studio_esp32c3.menu.PartitionScheme.minimal.build.partitions=minimal weact_studio_esp32c3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) @@ -43817,7 +44760,7 @@ aslcanx2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 aslcanx2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) aslcanx2.menu.PartitionScheme.huge_app.build.partitions=huge_app aslcanx2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -aslcanx2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +aslcanx2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) aslcanx2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs aslcanx2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 aslcanx2.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -44126,7 +45069,7 @@ elecrow_crowpanel_7.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104857 elecrow_crowpanel_7.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) elecrow_crowpanel_7.menu.PartitionScheme.huge_app.build.partitions=huge_app elecrow_crowpanel_7.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -elecrow_crowpanel_7.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +elecrow_crowpanel_7.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) elecrow_crowpanel_7.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs elecrow_crowpanel_7.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 @@ -44601,33 +45544,21 @@ codecell.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 codecell.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) codecell.menu.PartitionScheme.huge_app.build.partitions=huge_app codecell.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -codecell.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +codecell.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) codecell.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs codecell.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 -codecell.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) -codecell.menu.PartitionScheme.fatflash.build.partitions=ffat -codecell.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -codecell.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -codecell.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -codecell.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 codecell.menu.PartitionScheme.rainmaker=RainMaker 4MB codecell.menu.PartitionScheme.rainmaker.build.partitions=rainmaker codecell.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 codecell.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA codecell.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota codecell.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 -codecell.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB -codecell.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB -codecell.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 codecell.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs codecell.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr codecell.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 -codecell.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs -codecell.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB -codecell.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 codecell.menu.PartitionScheme.custom=Custom codecell.menu.PartitionScheme.custom.build.partitions= -codecell.menu.PartitionScheme.custom.upload.maximum_size=16777216 +codecell.menu.PartitionScheme.custom.upload.maximum_size=4194304 codecell.menu.CPUFreq.160=160MHz (WiFi) codecell.menu.CPUFreq.160.build.f_cpu=160000000L @@ -44760,7 +45691,7 @@ jczn_2432s028r.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 jczn_2432s028r.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) jczn_2432s028r.menu.PartitionScheme.huge_app.build.partitions=huge_app jczn_2432s028r.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -jczn_2432s028r.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +jczn_2432s028r.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) jczn_2432s028r.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs jczn_2432s028r.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 jczn_2432s028r.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -44967,10 +45898,11 @@ waveshare_esp32_s3_touch_amoled_241.menu.UploadMode.cdc.upload.use_1200bps_touch waveshare_esp32_s3_touch_amoled_241.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) - waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.defaultffat.build.partitions=default_ffat waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.no_ota.build.partitions=no_ota @@ -44987,7 +45919,7 @@ waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.noota_3gffat.upload.max waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -44999,18 +45931,12 @@ waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.rainmaker_8MB.upload.ma waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 - -waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_amoled_241.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -45173,6 +46099,8 @@ waveshare_esp32_s3_touch_lcd_43.menu.UploadMode.cdc.upload.use_1200bps_touch=tru waveshare_esp32_s3_touch_lcd_43.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -45192,7 +46120,7 @@ waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.noota_3gffat.upload.maximum waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -45204,17 +46132,12 @@ waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.rainmaker_8MB.upload.maximu waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_43.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -45372,6 +46295,8 @@ waveshare_esp32_s3_touch_lcd_43B.menu.UploadMode.cdc.upload.use_1200bps_touch=tr waveshare_esp32_s3_touch_lcd_43B.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -45391,7 +46316,7 @@ waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.noota_3gffat.upload.maximu waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -45403,17 +46328,12 @@ waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.rainmaker_8MB.upload.maxim waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_43B.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -45576,6 +46496,8 @@ waveshare_esp32_s3_touch_lcd_7.menu.UploadMode.cdc.upload.use_1200bps_touch=true waveshare_esp32_s3_touch_lcd_7.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -45595,7 +46517,7 @@ waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.noota_3gffat.upload.maximum_ waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -45607,17 +46529,12 @@ waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.rainmaker_8MB.upload.maximum waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_7.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -45775,6 +46692,8 @@ waveshare_esp32_s3_touch_lcd_5.menu.UploadMode.cdc.upload.use_1200bps_touch=true waveshare_esp32_s3_touch_lcd_5.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -45794,7 +46713,7 @@ waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.noota_3gffat.upload.maximum_ waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -45806,17 +46725,12 @@ waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.rainmaker_8MB.upload.maximum waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_5.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -45974,6 +46888,8 @@ waveshare_esp32_s3_touch_lcd_5B.menu.UploadMode.cdc.upload.use_1200bps_touch=tru waveshare_esp32_s3_touch_lcd_5B.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -45993,7 +46909,7 @@ waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.noota_3gffat.upload.maximum waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -46005,17 +46921,12 @@ waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.rainmaker_8MB.upload.maximu waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_5B.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -46173,6 +47084,8 @@ waveshare_esp32_s3_touch_lcd_4.menu.UploadMode.cdc.upload.use_1200bps_touch=true waveshare_esp32_s3_touch_lcd_4.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -46192,7 +47105,7 @@ waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.noota_3gffat.upload.maximum_ waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -46204,17 +47117,12 @@ waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.rainmaker_8MB.upload.maximum waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 -waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_lcd_4.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -46422,7 +47330,7 @@ waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.rainmaker_4MB.upload.maxim waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_185.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -46582,7 +47490,7 @@ cezerio_dev_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=104857 cezerio_dev_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) cezerio_dev_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app cezerio_dev_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -cezerio_dev_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +cezerio_dev_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) cezerio_dev_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs cezerio_dev_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 cezerio_dev_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -46599,7 +47507,7 @@ cezerio_dev_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zcz cezerio_dev_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 cezerio_dev_esp32c6.menu.PartitionScheme.custom=Custom cezerio_dev_esp32c6.menu.PartitionScheme.custom.build.partitions= -cezerio_dev_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +cezerio_dev_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 cezerio_dev_esp32c6.menu.CPUFreq.160=160MHz (WiFi) cezerio_dev_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -46752,7 +47660,7 @@ cezerio_mini_dev_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1 cezerio_mini_dev_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) cezerio_mini_dev_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app cezerio_mini_dev_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -cezerio_mini_dev_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +cezerio_mini_dev_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) cezerio_mini_dev_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs cezerio_mini_dev_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 cezerio_mini_dev_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -46769,7 +47677,7 @@ cezerio_mini_dev_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbe cezerio_mini_dev_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 cezerio_mini_dev_esp32c6.menu.PartitionScheme.custom=Custom cezerio_mini_dev_esp32c6.menu.PartitionScheme.custom.build.partitions= -cezerio_mini_dev_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=16777216 +cezerio_mini_dev_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 cezerio_mini_dev_esp32c6.menu.CPUFreq.160=160MHz (WiFi) cezerio_mini_dev_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L @@ -46999,7 +47907,7 @@ waveshare_esp32_s3_lcd_185.menu.PartitionScheme.rainmaker_4MB.upload.maximum_siz waveshare_esp32_s3_lcd_185.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_lcd_185.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_lcd_185.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_lcd_185.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_lcd_185.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_lcd_185.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_lcd_185.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_lcd_185.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -47236,7 +48144,7 @@ waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.rainmaker_4MB.upload.maxim waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_146.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -47473,7 +48381,7 @@ waveshare_esp32_s3_lcd_146.menu.PartitionScheme.rainmaker_4MB.upload.maximum_siz waveshare_esp32_s3_lcd_146.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_lcd_146.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_lcd_146.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_lcd_146.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_lcd_146.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_lcd_146.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_lcd_146.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_lcd_146.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -47710,7 +48618,7 @@ waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.rainmaker_4MB.upload.m waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_185_box.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -47943,7 +48851,7 @@ waveshare_esp32_s3_lcd_147.menu.PartitionScheme.rainmaker_4MB.upload.maximum_siz waveshare_esp32_s3_lcd_147.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_lcd_147.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_lcd_147.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_lcd_147.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_lcd_147.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_lcd_147.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_lcd_147.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_lcd_147.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -48180,7 +49088,7 @@ waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.rainmaker_4MB.upload.maximu waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_21.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -48417,7 +49325,7 @@ waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.rainmaker_4MB.upload.maximu waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_lcd_28.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -48657,7 +49565,7 @@ waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.rainmaker_4MB.upload.maximum_s waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_relay_6ch.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) @@ -48849,10 +49757,11 @@ waveshare_esp32_s3_touch_amoled_164.menu.UploadMode.cdc.upload.use_1200bps_touch waveshare_esp32_s3_touch_amoled_164.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) - waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.defaultffat.build.partitions=default_ffat waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.no_ota.build.partitions=no_ota @@ -48869,7 +49778,7 @@ waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.noota_3gffat.upload.max waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -48881,18 +49790,12 @@ waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.rainmaker_8MB.upload.ma waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 - -waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_amoled_164.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -49051,10 +49954,11 @@ waveshare_esp32_s3_touch_amoled_143.menu.UploadMode.cdc.upload.use_1200bps_touch waveshare_esp32_s3_touch_amoled_143.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) - waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.defaultffat.build.partitions=default_ffat waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.no_ota.build.partitions=no_ota @@ -49071,7 +49975,7 @@ waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.noota_3gffat.upload.max waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -49083,18 +49987,12 @@ waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.rainmaker_8MB.upload.ma waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 - -waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_amoled_143.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -49253,10 +50151,11 @@ waveshare_esp32_s3_touch_amoled_191.menu.UploadMode.cdc.upload.use_1200bps_touch waveshare_esp32_s3_touch_amoled_191.menu.UploadMode.cdc.upload.wait_for_upload_port=true waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.default.build.partitions=default waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) - waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.defaultffat.build.partitions=default_ffat waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.no_ota.build.partitions=no_ota @@ -49273,7 +50172,7 @@ waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.noota_3gffat.upload.max waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.huge_app.build.partitions=huge_app waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -49285,18 +50184,12 @@ waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.rainmaker_8MB.upload.ma waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.fatflash.build.partitions=ffat waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 - -waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) -waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB -waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 - waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.otanofs=OTA no FS (2MB APP with OTA) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.otanofs.build.custom_partitions=partitions_otanofs_4MB waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.otanofs.upload.maximum_size=2031616 waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.all_app=Max APP (4MB APP no OTA) waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.all_app.build.custom_partitions=partitions_all_app_4MB waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.all_app.upload.maximum_size=4128768 - waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.custom=Custom waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.custom.build.partitions= waveshare_esp32_s3_touch_amoled_191.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -49414,7 +50307,7 @@ Pcbcupid_GLYPH_C3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 Pcbcupid_GLYPH_C3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Pcbcupid_GLYPH_C3.menu.PartitionScheme.huge_app.build.partitions=huge_app Pcbcupid_GLYPH_C3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Pcbcupid_GLYPH_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +Pcbcupid_GLYPH_C3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) Pcbcupid_GLYPH_C3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs Pcbcupid_GLYPH_C3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 Pcbcupid_GLYPH_C3.menu.PartitionScheme.rainmaker=RainMaker 4MB @@ -49565,7 +50458,7 @@ Pcbcupid_GLYPH_H2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 Pcbcupid_GLYPH_H2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) Pcbcupid_GLYPH_H2.menu.PartitionScheme.huge_app.build.partitions=huge_app Pcbcupid_GLYPH_H2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -Pcbcupid_GLYPH_H2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +Pcbcupid_GLYPH_H2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) Pcbcupid_GLYPH_H2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs Pcbcupid_GLYPH_H2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 Pcbcupid_GLYPH_H2.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs @@ -49576,7 +50469,7 @@ Pcbcupid_GLYPH_H2.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr Pcbcupid_GLYPH_H2.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 Pcbcupid_GLYPH_H2.menu.PartitionScheme.custom=Custom Pcbcupid_GLYPH_H2.menu.PartitionScheme.custom.build.partitions= -Pcbcupid_GLYPH_H2.menu.PartitionScheme.custom.upload.maximum_size=16777216 +Pcbcupid_GLYPH_H2.menu.PartitionScheme.custom.upload.maximum_size=4194304 Pcbcupid_GLYPH_H2.menu.FlashMode.qio=QIO Pcbcupid_GLYPH_H2.menu.FlashMode.qio.build.flash_mode=dio @@ -49920,7 +50813,7 @@ yb_esp32s3_amp_v2.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 yb_esp32s3_amp_v2.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) yb_esp32s3_amp_v2.menu.PartitionScheme.huge_app.build.partitions=huge_app yb_esp32s3_amp_v2.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -yb_esp32s3_amp_v2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +yb_esp32s3_amp_v2.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) yb_esp32s3_amp_v2.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs yb_esp32s3_amp_v2.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 yb_esp32s3_amp_v2.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS) @@ -50130,7 +51023,7 @@ yb_esp32s3_amp_v3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 yb_esp32s3_amp_v3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) yb_esp32s3_amp_v3.menu.PartitionScheme.huge_app.build.partitions=huge_app yb_esp32s3_amp_v3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -yb_esp32s3_amp_v3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +yb_esp32s3_amp_v3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) yb_esp32s3_amp_v3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs yb_esp32s3_amp_v3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 yb_esp32s3_amp_v3.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS) @@ -50349,7 +51242,7 @@ yb_esp32s3_eth.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 yb_esp32s3_eth.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) yb_esp32s3_eth.menu.PartitionScheme.huge_app.build.partitions=huge_app yb_esp32s3_eth.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -yb_esp32s3_eth.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +yb_esp32s3_eth.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) yb_esp32s3_eth.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs yb_esp32s3_eth.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 yb_esp32s3_eth.menu.PartitionScheme.custom=Custom @@ -50559,7 +51452,7 @@ yb_esp32s3_drv.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 yb_esp32s3_drv.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) yb_esp32s3_drv.menu.PartitionScheme.huge_app.build.partitions=huge_app yb_esp32s3_drv.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -yb_esp32s3_drv.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +yb_esp32s3_drv.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) yb_esp32s3_drv.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs yb_esp32s3_drv.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 yb_esp32s3_drv.menu.PartitionScheme.max_app_8MB=Maximum APP (7.9MB APP No OTA/No FS) @@ -51082,7 +51975,7 @@ cyobot_v2_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 cyobot_v2_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) cyobot_v2_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app cyobot_v2_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 -cyobot_v2_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +cyobot_v2_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) cyobot_v2_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs cyobot_v2_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 cyobot_v2_esp32s3.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) @@ -51370,13 +52263,13 @@ kodedot.build.dfu_on_boot=0 kodedot.build.f_cpu=240000000L kodedot.build.flash_offset=0x400000 -kodedot.build.flash_size=16MB +kodedot.build.flash_size=32MB kodedot.build.flash_freq=80m kodedot.build.flash_mode=dio +kodedot.build.psram_type=opi kodedot.build.custom_partitions=kodedot_partitions -kodedot.build.psram_type=qspi kodedot.build.defines= kodedot.build.loop_core=-DARDUINO_RUNNING_CORE=1 @@ -51387,6 +52280,19 @@ kodedot.recipe.hooks.objcopy.postobjcopy.3.pattern_args= kodedot.recipe.output.save_file={build.project_name}.ino.bin +kodedot.menu.DebugLevel.none=None +kodedot.menu.DebugLevel.none.build.code_debug=0 +kodedot.menu.DebugLevel.error=Error +kodedot.menu.DebugLevel.error.build.code_debug=1 +kodedot.menu.DebugLevel.warn=Warn +kodedot.menu.DebugLevel.warn.build.code_debug=2 +kodedot.menu.DebugLevel.info=Info +kodedot.menu.DebugLevel.info.build.code_debug=3 +kodedot.menu.DebugLevel.debug=Debug +kodedot.menu.DebugLevel.debug.build.code_debug=4 +kodedot.menu.DebugLevel.verbose=Verbose +kodedot.menu.DebugLevel.verbose.build.code_debug=5 + ############################################################## # FED4 Board @@ -51573,3 +52479,1356 @@ fed4.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR fed4.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote ############################################################## + +# FoBE Quill ESP32S3 Mesh FH4R2 + +fobe_quill_esp32s3_mesh.name=FoBE Quill ESP32S3 Mesh +fobe_quill_esp32s3_mesh.vid.0=0x303a +fobe_quill_esp32s3_mesh.pid.0=0x82f4 +fobe_quill_esp32s3_mesh.vid.1=0x303a +fobe_quill_esp32s3_mesh.pid.1=0x82f5 +fobe_quill_esp32s3_mesh.vid.2=0x303a +fobe_quill_esp32s3_mesh.pid.2=0x82f6 + +fobe_quill_esp32s3_mesh.bootloader.tool=esptool_py +fobe_quill_esp32s3_mesh.bootloader.tool.default=esptool_py + +fobe_quill_esp32s3_mesh.upload.tool=esptool_py +fobe_quill_esp32s3_mesh.upload.tool.default=esptool_py +fobe_quill_esp32s3_mesh.upload.tool.network=esp_ota + +fobe_quill_esp32s3_mesh.upload.maximum_size=1310720 +fobe_quill_esp32s3_mesh.upload.maximum_data_size=327680 +fobe_quill_esp32s3_mesh.upload.flags= +fobe_quill_esp32s3_mesh.upload.extra_flags= +fobe_quill_esp32s3_mesh.upload.use_1200bps_touch=true +fobe_quill_esp32s3_mesh.upload.wait_for_upload_port=true + +fobe_quill_esp32s3_mesh.serial.disableDTR=false +fobe_quill_esp32s3_mesh.serial.disableRTS=false + +fobe_quill_esp32s3_mesh.build.tarch=xtensa +fobe_quill_esp32s3_mesh.build.bootloader_addr=0x0 +fobe_quill_esp32s3_mesh.build.target=esp32s3 +fobe_quill_esp32s3_mesh.build.mcu=esp32s3 +fobe_quill_esp32s3_mesh.build.core=esp32 +fobe_quill_esp32s3_mesh.build.variant=fobe_quill_esp32s3_mesh +fobe_quill_esp32s3_mesh.build.board=FOBE_QUILL_ESP32S3_MESH + +fobe_quill_esp32s3_mesh.build.usb_mode=0 +fobe_quill_esp32s3_mesh.build.cdc_on_boot=1 +fobe_quill_esp32s3_mesh.build.msc_on_boot=0 +fobe_quill_esp32s3_mesh.build.dfu_on_boot=0 +fobe_quill_esp32s3_mesh.build.f_cpu=240000000L +fobe_quill_esp32s3_mesh.build.flash_size=4MB +fobe_quill_esp32s3_mesh.build.flash_freq=80m +fobe_quill_esp32s3_mesh.build.flash_mode=qio +fobe_quill_esp32s3_mesh.build.boot=qio +fobe_quill_esp32s3_mesh.build.boot_freq=80m +fobe_quill_esp32s3_mesh.build.partitions=ffat +fobe_quill_esp32s3_mesh.build.defines= +fobe_quill_esp32s3_mesh.build.loop_core= +fobe_quill_esp32s3_mesh.build.event_core= +fobe_quill_esp32s3_mesh.build.psram_type=qspi +fobe_quill_esp32s3_mesh.build.memory_type={build.boot}_{build.psram_type} + +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.default=Disabled +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.default.build.copy_jtag_files=0 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin=Integrated USB JTAG +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external=FTDI Adapter +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external.build.copy_jtag_files=1 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge=ESP USB Bridge +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled=Disabled +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled.build.defines= +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled.build.psram_type=qspi +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled=QSPI PSRAM +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled.build.psram_type=qspi + +fobe_quill_esp32s3_mesh.menu.FlashMode.qio=QIO 80MHz +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.flash_mode=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.boot=qio +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.boot_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.flash_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.dio=DIO 80MHz +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.flash_mode=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.boot=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.boot_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.flash_freq=80m + +fobe_quill_esp32s3_mesh.menu.FlashSize.4M=4MB (32Mb) +fobe_quill_esp32s3_mesh.menu.FlashSize.4M.build.flash_size=4MB + +fobe_quill_esp32s3_mesh.menu.LoopCore.1=Core 1 +fobe_quill_esp32s3_mesh.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +fobe_quill_esp32s3_mesh.menu.LoopCore.0=Core 0 +fobe_quill_esp32s3_mesh.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +fobe_quill_esp32s3_mesh.menu.EventsCore.1=Core 1 +fobe_quill_esp32s3_mesh.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +fobe_quill_esp32s3_mesh.menu.EventsCore.0=Core 0 +fobe_quill_esp32s3_mesh.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +fobe_quill_esp32s3_mesh.menu.USBMode.hwcdc=Hardware CDC and JTAG +fobe_quill_esp32s3_mesh.menu.USBMode.hwcdc.build.usb_mode=1 +fobe_quill_esp32s3_mesh.menu.USBMode.default=USB-OTG (TinyUSB) +fobe_quill_esp32s3_mesh.menu.USBMode.default.build.usb_mode=0 + +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.default=Enabled +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.default.build.cdc_on_boot=1 +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.cdc=Disabled +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.default=Disabled +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.default.build.msc_on_boot=0 +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.default=Disabled +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.default.build.dfu_on_boot=0 +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +fobe_quill_esp32s3_mesh.menu.UploadMode.default=UART0 / Hardware CDC +fobe_quill_esp32s3_mesh.menu.UploadMode.default.upload.use_1200bps_touch=false +fobe_quill_esp32s3_mesh.menu.UploadMode.default.upload.wait_for_upload_port=false +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc.upload.use_1200bps_touch=true +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +fobe_quill_esp32s3_mesh.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.default.build.partitions=default +fobe_quill_esp32s3_mesh.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.minimal.build.partitions=minimal +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota.build.partitions=no_ota +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app.build.partitions=huge_app +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 + +fobe_quill_esp32s3_mesh.menu.CPUFreq.240=240MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.240.build.f_cpu=240000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.160=160MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.160.build.f_cpu=160000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.80=80MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.80.build.f_cpu=80000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.40=40MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.40.build.f_cpu=40000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.20=20MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.20.build.f_cpu=20000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.10=10MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.10.build.f_cpu=10000000L + +fobe_quill_esp32s3_mesh.menu.UploadSpeed.921600=921600 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.921600.upload.speed=921600 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.115200=115200 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.115200.upload.speed=115200 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.256000.windows=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.256000.upload.speed=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400.windows.upload.speed=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400=230400 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400.upload.speed=230400 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.linux=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.macosx=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.upload.speed=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.512000.windows=512000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.512000.upload.speed=512000 + +fobe_quill_esp32s3_mesh.menu.DebugLevel.none=None +fobe_quill_esp32s3_mesh.menu.DebugLevel.none.build.code_debug=0 +fobe_quill_esp32s3_mesh.menu.DebugLevel.error=Error +fobe_quill_esp32s3_mesh.menu.DebugLevel.error.build.code_debug=1 +fobe_quill_esp32s3_mesh.menu.DebugLevel.warn=Warn +fobe_quill_esp32s3_mesh.menu.DebugLevel.warn.build.code_debug=2 +fobe_quill_esp32s3_mesh.menu.DebugLevel.info=Info +fobe_quill_esp32s3_mesh.menu.DebugLevel.info.build.code_debug=3 +fobe_quill_esp32s3_mesh.menu.DebugLevel.debug=Debug +fobe_quill_esp32s3_mesh.menu.DebugLevel.debug.build.code_debug=4 +fobe_quill_esp32s3_mesh.menu.DebugLevel.verbose=Verbose +fobe_quill_esp32s3_mesh.menu.DebugLevel.verbose.build.code_debug=5 + +fobe_quill_esp32s3_mesh.menu.EraseFlash.none=Disabled +fobe_quill_esp32s3_mesh.menu.EraseFlash.none.upload.erase_cmd= +fobe_quill_esp32s3_mesh.menu.EraseFlash.all=Enabled +fobe_quill_esp32s3_mesh.menu.EraseFlash.all.upload.erase_cmd=-e + +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default=Disabled +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default.build.zigbee_mode= +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default.build.zigbee_libs= +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +twinaiot.name=Twin AIoT Module + +twinaiot.bootloader.tool=esptool_py +twinaiot.bootloader.tool.default=esptool_py + +twinaiot.upload.tool=esptool_py +twinaiot.upload.tool.default=esptool_py +twinaiot.upload.tool.network=esp_ota + +twinaiot.upload.maximum_size=1310720 +twinaiot.upload.maximum_data_size=327680 +twinaiot.upload.flags= +twinaiot.upload.extra_flags= +twinaiot.upload.use_1200bps_touch=false +twinaiot.upload.wait_for_upload_port=false + +twinaiot.serial.disableDTR=false +twinaiot.serial.disableRTS=false + +twinaiot.build.tarch=xtensa +twinaiot.build.bootloader_addr=0x0 +twinaiot.build.target=esp32s3 +twinaiot.build.mcu=esp32s3 +twinaiot.build.core=esp32 +twinaiot.build.variant=twinaiot +twinaiot.build.board=TWIN_AIOT + +twinaiot.build.usb_mode=1 +twinaiot.build.cdc_on_boot=0 +twinaiot.build.msc_on_boot=0 +twinaiot.build.dfu_on_boot=0 +twinaiot.build.f_cpu=240000000L +twinaiot.build.flash_size=4MB +twinaiot.build.flash_freq=80m +twinaiot.build.flash_mode=dio +twinaiot.build.boot=qio +twinaiot.build.boot_freq=80m +twinaiot.build.partitions=default +twinaiot.build.defines= +twinaiot.build.loop_core= +twinaiot.build.event_core= +twinaiot.build.psram_type=qspi +twinaiot.build.memory_type={build.boot}_{build.psram_type} + +twinaiot.menu.JTAGAdapter.default=Disabled +twinaiot.menu.JTAGAdapter.default.build.copy_jtag_files=0 +twinaiot.menu.JTAGAdapter.builtin=Integrated USB JTAG +twinaiot.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +twinaiot.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +twinaiot.menu.JTAGAdapter.external=FTDI Adapter +twinaiot.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +twinaiot.menu.JTAGAdapter.external.build.copy_jtag_files=1 +twinaiot.menu.JTAGAdapter.bridge=ESP USB Bridge +twinaiot.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +twinaiot.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +twinaiot.menu.PSRAM.disabled=Disabled +twinaiot.menu.PSRAM.disabled.build.defines= +twinaiot.menu.PSRAM.disabled.build.psram_type=qspi +twinaiot.menu.PSRAM.enabled=QSPI PSRAM +twinaiot.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +twinaiot.menu.PSRAM.enabled.build.psram_type=qspi + +twinaiot.menu.FlashMode.qio=QIO 80MHz +twinaiot.menu.FlashMode.qio.build.flash_mode=dio +twinaiot.menu.FlashMode.qio.build.boot=qio +twinaiot.menu.FlashMode.qio.build.boot_freq=80m +twinaiot.menu.FlashMode.qio.build.flash_freq=80m +twinaiot.menu.FlashMode.qio120=QIO 120MHz +twinaiot.menu.FlashMode.qio120.build.flash_mode=dio +twinaiot.menu.FlashMode.qio120.build.boot=qio +twinaiot.menu.FlashMode.qio120.build.boot_freq=120m +twinaiot.menu.FlashMode.qio120.build.flash_freq=80m +twinaiot.menu.FlashMode.dio=DIO 80MHz +twinaiot.menu.FlashMode.dio.build.flash_mode=dio +twinaiot.menu.FlashMode.dio.build.boot=dio +twinaiot.menu.FlashMode.dio.build.boot_freq=80m +twinaiot.menu.FlashMode.dio.build.flash_freq=80m + + +twinaiot.menu.FlashSize.4M=4MB (32Mb) +twinaiot.menu.FlashSize.4M.build.flash_size=4MB + +twinaiot.menu.CPUFreq.240=240MHz (WiFi) +twinaiot.menu.CPUFreq.240.build.f_cpu=240000000L +twinaiot.menu.CPUFreq.160=160MHz (WiFi) +twinaiot.menu.CPUFreq.160.build.f_cpu=160000000L +twinaiot.menu.CPUFreq.80=80MHz (WiFi) +twinaiot.menu.CPUFreq.80.build.f_cpu=80000000L +twinaiot.menu.CPUFreq.40=40MHz +twinaiot.menu.CPUFreq.40.build.f_cpu=40000000L +twinaiot.menu.CPUFreq.20=20MHz +twinaiot.menu.CPUFreq.20.build.f_cpu=20000000L +twinaiot.menu.CPUFreq.10=10MHz +twinaiot.menu.CPUFreq.10.build.f_cpu=10000000L + +twinaiot.menu.UploadSpeed.921600=921600 +twinaiot.menu.UploadSpeed.921600.upload.speed=921600 +twinaiot.menu.UploadSpeed.115200=115200 +twinaiot.menu.UploadSpeed.115200.upload.speed=115200 +twinaiot.menu.UploadSpeed.256000.windows=256000 +twinaiot.menu.UploadSpeed.256000.upload.speed=256000 +twinaiot.menu.UploadSpeed.230400.windows.upload.speed=256000 +twinaiot.menu.UploadSpeed.230400=230400 +twinaiot.menu.UploadSpeed.230400.upload.speed=230400 +twinaiot.menu.UploadSpeed.460800.linux=460800 +twinaiot.menu.UploadSpeed.460800.macosx=460800 +twinaiot.menu.UploadSpeed.460800.upload.speed=460800 +twinaiot.menu.UploadSpeed.512000.windows=512000 +twinaiot.menu.UploadSpeed.512000.upload.speed=512000 + +twinaiot.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +twinaiot.menu.PartitionScheme.default.build.partitions=default +twinaiot.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +twinaiot.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +twinaiot.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +twinaiot.menu.PartitionScheme.minimal.build.partitions=minimal +twinaiot.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +twinaiot.menu.PartitionScheme.no_fs.build.partitions=no_fs +twinaiot.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +twinaiot.menu.PartitionScheme.no_ota.build.partitions=no_ota +twinaiot.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +twinaiot.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +twinaiot.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +twinaiot.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +twinaiot.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +twinaiot.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +twinaiot.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +twinaiot.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +twinaiot.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +twinaiot.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +twinaiot.menu.PartitionScheme.huge_app.build.partitions=huge_app +twinaiot.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +twinaiot.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +twinaiot.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +twinaiot.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +twinaiot.menu.PartitionScheme.rainmaker=RainMaker 4MB +twinaiot.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +twinaiot.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +twinaiot.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +twinaiot.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +twinaiot.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +twinaiot.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +twinaiot.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +twinaiot.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +twinaiot.menu.PartitionScheme.custom=Custom +twinaiot.menu.PartitionScheme.custom.build.partitions= +twinaiot.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +twinaiot.menu.LoopCore.1=Core 1 +twinaiot.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +twinaiot.menu.LoopCore.0=Core 0 +twinaiot.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +twinaiot.menu.EventsCore.1=Core 1 +twinaiot.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +twinaiot.menu.EventsCore.0=Core 0 +twinaiot.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +twinaiot.menu.USBMode.hwcdc=Hardware CDC and JTAG +twinaiot.menu.USBMode.hwcdc.build.usb_mode=1 +twinaiot.menu.USBMode.default=USB-OTG (TinyUSB) +twinaiot.menu.USBMode.default.build.usb_mode=0 + +twinaiot.menu.CDCOnBoot.default=Disabled +twinaiot.menu.CDCOnBoot.default.build.cdc_on_boot=0 +twinaiot.menu.CDCOnBoot.cdc=Enabled +twinaiot.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +twinaiot.menu.MSCOnBoot.default=Disabled +twinaiot.menu.MSCOnBoot.default.build.msc_on_boot=0 +twinaiot.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +twinaiot.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +twinaiot.menu.DFUOnBoot.default=Disabled +twinaiot.menu.DFUOnBoot.default.build.dfu_on_boot=0 +twinaiot.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +twinaiot.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +twinaiot.menu.UploadMode.default=UART0 / Hardware CDC +twinaiot.menu.UploadMode.default.upload.use_1200bps_touch=false +twinaiot.menu.UploadMode.default.upload.wait_for_upload_port=false +twinaiot.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +twinaiot.menu.UploadMode.cdc.upload.use_1200bps_touch=true +twinaiot.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +twinaiot.menu.DebugLevel.none=None +twinaiot.menu.DebugLevel.none.build.code_debug=0 +twinaiot.menu.DebugLevel.error=Error +twinaiot.menu.DebugLevel.error.build.code_debug=1 +twinaiot.menu.DebugLevel.warn=Warn +twinaiot.menu.DebugLevel.warn.build.code_debug=2 +twinaiot.menu.DebugLevel.info=Info +twinaiot.menu.DebugLevel.info.build.code_debug=3 +twinaiot.menu.DebugLevel.debug=Debug +twinaiot.menu.DebugLevel.debug.build.code_debug=4 +twinaiot.menu.DebugLevel.verbose=Verbose +twinaiot.menu.DebugLevel.verbose.build.code_debug=5 + +twinaiot.menu.EraseFlash.none=Disabled +twinaiot.menu.EraseFlash.none.upload.erase_cmd= +twinaiot.menu.EraseFlash.all=Enabled +twinaiot.menu.EraseFlash.all.upload.erase_cmd=-e + +twinaiot.menu.ZigbeeMode.default=Disabled +twinaiot.menu.ZigbeeMode.default.build.zigbee_mode= +twinaiot.menu.ZigbeeMode.default.build.zigbee_libs= +twinaiot.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +twinaiot.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +twinaiot.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +esp32p4_4ds_mipi.name=4D Systems ESP32-P4 MIPI Displays + +esp32p4_4ds_mipi.bootloader.tool=esptool_py +esp32p4_4ds_mipi.bootloader.tool.default=esptool_py + +esp32p4_4ds_mipi.upload.tool=esptool_py +esp32p4_4ds_mipi.upload.tool.default=esptool_py +esp32p4_4ds_mipi.upload.tool.network=esp_ota + +esp32p4_4ds_mipi.upload.maximum_size=1310720 +esp32p4_4ds_mipi.upload.maximum_data_size=327680 +esp32p4_4ds_mipi.upload.flags= +esp32p4_4ds_mipi.upload.extra_flags= +esp32p4_4ds_mipi.upload.use_1200bps_touch=false +esp32p4_4ds_mipi.upload.wait_for_upload_port=false + +esp32p4_4ds_mipi.serial.disableDTR=false +esp32p4_4ds_mipi.serial.disableRTS=false + +esp32p4_4ds_mipi.build.tarch=riscv32 +esp32p4_4ds_mipi.build.target=esp +esp32p4_4ds_mipi.build.mcu=esp32p4 +esp32p4_4ds_mipi.build.core=esp32 +esp32p4_4ds_mipi.build.variant=esp32p4_4ds_mipi +esp32p4_4ds_mipi.build.board=ESP32P4_4DS_MIPI +esp32p4_4ds_mipi.build.bootloader_addr=0x2000 + +esp32p4_4ds_mipi.build.usb_mode=0 +esp32p4_4ds_mipi.build.cdc_on_boot=0 +esp32p4_4ds_mipi.build.msc_on_boot=0 +esp32p4_4ds_mipi.build.dfu_on_boot=0 +esp32p4_4ds_mipi.build.f_cpu=360000000L +esp32p4_4ds_mipi.build.flash_size=32MB +esp32p4_4ds_mipi.build.flash_freq=80m +esp32p4_4ds_mipi.build.img_freq=80m +esp32p4_4ds_mipi.build.flash_mode=qio +esp32p4_4ds_mipi.build.boot=qio +esp32p4_4ds_mipi.build.partitions=app5M_fat24M_32MB + +## IDE 2.0 Seems to not update the value +esp32p4_4ds_mipi.menu.JTAGAdapter.default=Disabled +esp32p4_4ds_mipi.menu.JTAGAdapter.default.build.copy_jtag_files=0 +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin=Integrated USB JTAG +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin.build.openocdscript=esp32p4-builtin.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +esp32p4_4ds_mipi.menu.JTAGAdapter.external=FTDI Adapter +esp32p4_4ds_mipi.menu.JTAGAdapter.external.build.openocdscript=esp32p4-ftdi.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.external.build.copy_jtag_files=1 +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge=ESP USB Bridge +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge.build.openocdscript=esp32p4-bridge.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +esp32p4_4ds_mipi.menu.USBMode.default=USB-OTG (TinyUSB) +esp32p4_4ds_mipi.menu.USBMode.default.build.usb_mode=0 +esp32p4_4ds_mipi.menu.USBMode.hwcdc=Hardware CDC and JTAG +esp32p4_4ds_mipi.menu.USBMode.hwcdc.build.usb_mode=1 + +esp32p4_4ds_mipi.menu.CDCOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.CDCOnBoot.default.build.cdc_on_boot=0 +esp32p4_4ds_mipi.menu.CDCOnBoot.cdc=Enabled +esp32p4_4ds_mipi.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +esp32p4_4ds_mipi.menu.MSCOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.MSCOnBoot.default.build.msc_on_boot=0 +esp32p4_4ds_mipi.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +esp32p4_4ds_mipi.menu.DFUOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.DFUOnBoot.default.build.dfu_on_boot=0 +esp32p4_4ds_mipi.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +esp32p4_4ds_mipi.menu.UploadMode.default=UART0 / Hardware CDC +esp32p4_4ds_mipi.menu.UploadMode.default.upload.use_1200bps_touch=false +esp32p4_4ds_mipi.menu.UploadMode.default.upload.wait_for_upload_port=false +esp32p4_4ds_mipi.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +esp32p4_4ds_mipi.menu.UploadMode.cdc.upload.use_1200bps_touch=true +esp32p4_4ds_mipi.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 + +## From https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/kconfig.html#config-esp-default-cpu-freq-mhz +esp32p4_4ds_mipi.menu.CPUFreq.360=360MHz +esp32p4_4ds_mipi.menu.CPUFreq.360.build.f_cpu=360000000L +esp32p4_4ds_mipi.menu.CPUFreq.40=40MHz +esp32p4_4ds_mipi.menu.CPUFreq.40.build.f_cpu=40000000L + +esp32p4_4ds_mipi.menu.UploadSpeed.921600=921600 +esp32p4_4ds_mipi.menu.UploadSpeed.921600.upload.speed=921600 +esp32p4_4ds_mipi.menu.UploadSpeed.115200=115200 +esp32p4_4ds_mipi.menu.UploadSpeed.115200.upload.speed=115200 +esp32p4_4ds_mipi.menu.UploadSpeed.256000.windows=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.256000.upload.speed=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.230400.windows.upload.speed=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.230400=230400 +esp32p4_4ds_mipi.menu.UploadSpeed.230400.upload.speed=230400 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.linux=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.macosx=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.upload.speed=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.512000.windows=512000 +esp32p4_4ds_mipi.menu.UploadSpeed.512000.upload.speed=512000 + +esp32p4_4ds_mipi.menu.DebugLevel.none=None +esp32p4_4ds_mipi.menu.DebugLevel.none.build.code_debug=0 +esp32p4_4ds_mipi.menu.DebugLevel.error=Error +esp32p4_4ds_mipi.menu.DebugLevel.error.build.code_debug=1 +esp32p4_4ds_mipi.menu.DebugLevel.warn=Warn +esp32p4_4ds_mipi.menu.DebugLevel.warn.build.code_debug=2 +esp32p4_4ds_mipi.menu.DebugLevel.info=Info +esp32p4_4ds_mipi.menu.DebugLevel.info.build.code_debug=3 +esp32p4_4ds_mipi.menu.DebugLevel.debug=Debug +esp32p4_4ds_mipi.menu.DebugLevel.debug.build.code_debug=4 +esp32p4_4ds_mipi.menu.DebugLevel.verbose=Verbose +esp32p4_4ds_mipi.menu.DebugLevel.verbose.build.code_debug=5 + +esp32p4_4ds_mipi.menu.EraseFlash.none=Disabled +esp32p4_4ds_mipi.menu.EraseFlash.none.upload.erase_cmd= +esp32p4_4ds_mipi.menu.EraseFlash.all=Enabled +esp32p4_4ds_mipi.menu.EraseFlash.all.upload.erase_cmd=-e + +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70=ESP32-P4-70 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70.build.DisplayModel=ESP32P4_70 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct=ESP32-P4-70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct.build.DisplayModel=ESP32P4_70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct_clb=ESP32-P4-70CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct_clb.build.DisplayModel=ESP32P4_70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80=ESP32-P4-80 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80.build.DisplayModel=ESP32P4_80 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct=ESP32-P4-80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct.build.DisplayModel=ESP32P4_80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct_clb=ESP32-P4-80CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct_clb.build.DisplayModel=ESP32P4_80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90=ESP32-P4-90 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90.build.DisplayModel=ESP32P4_90 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct=ESP32-P4-90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct.build.DisplayModel=ESP32P4_90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct_clb=ESP32-P4-90CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct_clb.build.DisplayModel=ESP32P4_90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101=ESP32-P4-101 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101.build.DisplayModel=ESP32P4_101 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct=ESP32-P4-101CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct.build.DisplayModel=ESP32P4_101CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct_clb=ESP32-P4-101CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct_clb.build.DisplayModel=ESP32P4_101CT + +esp32p4_4ds_mipi.build.defines=-DBOARD_HAS_PSRAM -D{build.board} -D{build.DisplayModel} + +############################################################## + +# Axiometa PIXIE M1 - Based on ESP32-S3-Mini-N4R2 +# 4MB Quad SPI Flash, 2MB Quad SPI PSRAM + +axiometa_pixie_m1.name=Axiometa PIXIE M1 + +axiometa_pixie_m1.bootloader.tool=esptool_py +axiometa_pixie_m1.bootloader.tool.default=esptool_py + +axiometa_pixie_m1.upload.tool=esptool_py +axiometa_pixie_m1.upload.tool.default=esptool_py +axiometa_pixie_m1.upload.tool.network=esp_ota + +axiometa_pixie_m1.upload.maximum_size=1310720 +axiometa_pixie_m1.upload.maximum_data_size=327680 +axiometa_pixie_m1.upload.flags= +axiometa_pixie_m1.upload.extra_flags= +axiometa_pixie_m1.upload.use_1200bps_touch=false +axiometa_pixie_m1.upload.wait_for_upload_port=false + +axiometa_pixie_m1.serial.disableDTR=false +axiometa_pixie_m1.serial.disableRTS=false + +axiometa_pixie_m1.build.tarch=xtensa +axiometa_pixie_m1.build.bootloader_addr=0x0 +axiometa_pixie_m1.build.target=esp32s3 +axiometa_pixie_m1.build.mcu=esp32s3 +axiometa_pixie_m1.build.core=esp32 +axiometa_pixie_m1.build.variant=axiometa_pixie_m1 +axiometa_pixie_m1.build.board=AXIOMETA_PIXIE_M1 + +# Hardware Configuration (ESP32-S3-Mini-N4R2) +axiometa_pixie_m1.build.usb_mode=1 +axiometa_pixie_m1.build.cdc_on_boot=1 +axiometa_pixie_m1.build.msc_on_boot=0 +axiometa_pixie_m1.build.dfu_on_boot=0 +axiometa_pixie_m1.build.f_cpu=240000000L +axiometa_pixie_m1.build.flash_size=4MB +axiometa_pixie_m1.build.flash_freq=80m +axiometa_pixie_m1.build.flash_mode=dio +axiometa_pixie_m1.build.boot=qio +axiometa_pixie_m1.build.boot_freq=80m +axiometa_pixie_m1.build.partitions=default +axiometa_pixie_m1.build.defines=-DBOARD_HAS_PSRAM +axiometa_pixie_m1.build.loop_core= +axiometa_pixie_m1.build.event_core= +axiometa_pixie_m1.build.psram_type=qspi +axiometa_pixie_m1.build.memory_type={build.boot}_{build.psram_type} + +## JTAG Adapter - N4R2 Compatible +axiometa_pixie_m1.menu.JTAGAdapter.default=Disabled +axiometa_pixie_m1.menu.JTAGAdapter.default.build.copy_jtag_files=0 +axiometa_pixie_m1.menu.JTAGAdapter.builtin=Integrated USB JTAG +axiometa_pixie_m1.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +axiometa_pixie_m1.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +axiometa_pixie_m1.menu.JTAGAdapter.external=FTDI Adapter +axiometa_pixie_m1.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +axiometa_pixie_m1.menu.JTAGAdapter.external.build.copy_jtag_files=1 +axiometa_pixie_m1.menu.JTAGAdapter.bridge=ESP USB Bridge +axiometa_pixie_m1.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +axiometa_pixie_m1.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +## PSRAM - N4R2 has 2MB QSPI PSRAM +axiometa_pixie_m1.menu.PSRAM.enabled=QSPI PSRAM (Enabled) +axiometa_pixie_m1.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +axiometa_pixie_m1.menu.PSRAM.enabled.build.psram_type=qspi +axiometa_pixie_m1.menu.PSRAM.disabled=Disabled +axiometa_pixie_m1.menu.PSRAM.disabled.build.defines= +axiometa_pixie_m1.menu.PSRAM.disabled.build.psram_type=qspi + +## Flash Mode - N4R2 Compatible (QSPI Flash) +axiometa_pixie_m1.menu.FlashMode.qio=QIO 80MHz +axiometa_pixie_m1.menu.FlashMode.qio.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.qio.build.boot=qio +axiometa_pixie_m1.menu.FlashMode.qio.build.boot_freq=80m +axiometa_pixie_m1.menu.FlashMode.qio.build.flash_freq=80m +axiometa_pixie_m1.menu.FlashMode.qio120=QIO 120MHz +axiometa_pixie_m1.menu.FlashMode.qio120.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.qio120.build.boot=qio +axiometa_pixie_m1.menu.FlashMode.qio120.build.boot_freq=120m +axiometa_pixie_m1.menu.FlashMode.qio120.build.flash_freq=80m +axiometa_pixie_m1.menu.FlashMode.dio=DIO 80MHz +axiometa_pixie_m1.menu.FlashMode.dio.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.dio.build.boot=dio +axiometa_pixie_m1.menu.FlashMode.dio.build.boot_freq=80m +axiometa_pixie_m1.menu.FlashMode.dio.build.flash_freq=80m + +## CPU Core Assignment +axiometa_pixie_m1.menu.LoopCore.1=Core 1 +axiometa_pixie_m1.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +axiometa_pixie_m1.menu.LoopCore.0=Core 0 +axiometa_pixie_m1.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +axiometa_pixie_m1.menu.EventsCore.1=Core 1 +axiometa_pixie_m1.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +axiometa_pixie_m1.menu.EventsCore.0=Core 0 +axiometa_pixie_m1.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +## USB Mode - Both modes work on N4R2 +axiometa_pixie_m1.menu.USBMode.hwcdc=Hardware CDC and JTAG +axiometa_pixie_m1.menu.USBMode.hwcdc.build.usb_mode=1 +axiometa_pixie_m1.menu.USBMode.default=USB-OTG (TinyUSB) +axiometa_pixie_m1.menu.USBMode.default.build.usb_mode=0 + +## CDC On Boot +axiometa_pixie_m1.menu.CDCOnBoot.cdc=Enabled +axiometa_pixie_m1.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +axiometa_pixie_m1.menu.CDCOnBoot.default=Disabled +axiometa_pixie_m1.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +## MSC On Boot (Only works with USB-OTG mode) +axiometa_pixie_m1.menu.MSCOnBoot.default=Disabled +axiometa_pixie_m1.menu.MSCOnBoot.default.build.msc_on_boot=0 +axiometa_pixie_m1.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +axiometa_pixie_m1.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +## DFU On Boot (Only works with USB-OTG mode) +axiometa_pixie_m1.menu.DFUOnBoot.default=Disabled +axiometa_pixie_m1.menu.DFUOnBoot.default.build.dfu_on_boot=0 +axiometa_pixie_m1.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +axiometa_pixie_m1.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +## Upload Mode +axiometa_pixie_m1.menu.UploadMode.default=UART0 / Hardware CDC +axiometa_pixie_m1.menu.UploadMode.default.upload.use_1200bps_touch=false +axiometa_pixie_m1.menu.UploadMode.default.upload.wait_for_upload_port=false +axiometa_pixie_m1.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +axiometa_pixie_m1.menu.UploadMode.cdc.upload.use_1200bps_touch=true +axiometa_pixie_m1.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +## Partition Schemes - 4MB Flash Compatible Only +axiometa_pixie_m1.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.default.build.partitions=default +axiometa_pixie_m1.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +axiometa_pixie_m1.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.minimal.build.partitions=minimal +axiometa_pixie_m1.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +axiometa_pixie_m1.menu.PartitionScheme.no_fs.build.partitions=no_fs +axiometa_pixie_m1.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +axiometa_pixie_m1.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.no_ota.build.partitions=no_ota +axiometa_pixie_m1.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +axiometa_pixie_m1.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +axiometa_pixie_m1.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +axiometa_pixie_m1.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.huge_app.build.partitions=huge_app +axiometa_pixie_m1.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +axiometa_pixie_m1.menu.PartitionScheme.rainmaker=RainMaker 4MB +axiometa_pixie_m1.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +axiometa_pixie_m1.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +axiometa_pixie_m1.menu.PartitionScheme.custom=Custom +axiometa_pixie_m1.menu.PartitionScheme.custom.build.partitions= +axiometa_pixie_m1.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +## CPU Frequency +axiometa_pixie_m1.menu.CPUFreq.240=240MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.240.build.f_cpu=240000000L +axiometa_pixie_m1.menu.CPUFreq.160=160MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.160.build.f_cpu=160000000L +axiometa_pixie_m1.menu.CPUFreq.80=80MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.80.build.f_cpu=80000000L +axiometa_pixie_m1.menu.CPUFreq.40=40MHz +axiometa_pixie_m1.menu.CPUFreq.40.build.f_cpu=40000000L +axiometa_pixie_m1.menu.CPUFreq.20=20MHz +axiometa_pixie_m1.menu.CPUFreq.20.build.f_cpu=20000000L +axiometa_pixie_m1.menu.CPUFreq.10=10MHz +axiometa_pixie_m1.menu.CPUFreq.10.build.f_cpu=10000000L + +## Upload Speed +axiometa_pixie_m1.menu.UploadSpeed.921600=921600 +axiometa_pixie_m1.menu.UploadSpeed.921600.upload.speed=921600 +axiometa_pixie_m1.menu.UploadSpeed.115200=115200 +axiometa_pixie_m1.menu.UploadSpeed.115200.upload.speed=115200 +axiometa_pixie_m1.menu.UploadSpeed.256000.windows=256000 +axiometa_pixie_m1.menu.UploadSpeed.256000.upload.speed=256000 +axiometa_pixie_m1.menu.UploadSpeed.230400.windows.upload.speed=256000 +axiometa_pixie_m1.menu.UploadSpeed.230400=230400 +axiometa_pixie_m1.menu.UploadSpeed.230400.upload.speed=230400 +axiometa_pixie_m1.menu.UploadSpeed.460800.linux=460800 +axiometa_pixie_m1.menu.UploadSpeed.460800.macosx=460800 +axiometa_pixie_m1.menu.UploadSpeed.460800.upload.speed=460800 +axiometa_pixie_m1.menu.UploadSpeed.512000.windows=512000 +axiometa_pixie_m1.menu.UploadSpeed.512000.upload.speed=512000 + +## Debug Level +axiometa_pixie_m1.menu.DebugLevel.none=None +axiometa_pixie_m1.menu.DebugLevel.none.build.code_debug=0 +axiometa_pixie_m1.menu.DebugLevel.error=Error +axiometa_pixie_m1.menu.DebugLevel.error.build.code_debug=1 +axiometa_pixie_m1.menu.DebugLevel.warn=Warn +axiometa_pixie_m1.menu.DebugLevel.warn.build.code_debug=2 +axiometa_pixie_m1.menu.DebugLevel.info=Info +axiometa_pixie_m1.menu.DebugLevel.info.build.code_debug=3 +axiometa_pixie_m1.menu.DebugLevel.debug=Debug +axiometa_pixie_m1.menu.DebugLevel.debug.build.code_debug=4 +axiometa_pixie_m1.menu.DebugLevel.verbose=Verbose +axiometa_pixie_m1.menu.DebugLevel.verbose.build.code_debug=5 + +## Erase Flash +axiometa_pixie_m1.menu.EraseFlash.none=Disabled +axiometa_pixie_m1.menu.EraseFlash.none.upload.erase_cmd= +axiometa_pixie_m1.menu.EraseFlash.all=Enabled +axiometa_pixie_m1.menu.EraseFlash.all.upload.erase_cmd=-e + +## Zigbee Mode +axiometa_pixie_m1.menu.ZigbeeMode.default=Disabled +axiometa_pixie_m1.menu.ZigbeeMode.default.build.zigbee_mode= +axiometa_pixie_m1.menu.ZigbeeMode.default.build.zigbee_libs= +axiometa_pixie_m1.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +axiometa_pixie_m1.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +axiometa_pixie_m1.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +soldered_nula_deepsleep_esp32s3.name=Soldered NULA DeepSleep ESP32S3 + +soldered_nula_deepsleep_esp32s3.bootloader.tool=esptool_py +soldered_nula_deepsleep_esp32s3.bootloader.tool.default=esptool_py + +soldered_nula_deepsleep_esp32s3.upload.tool=esptool_py +soldered_nula_deepsleep_esp32s3.upload.tool.default=esptool_py +soldered_nula_deepsleep_esp32s3.upload.tool.network=esp_ota + +soldered_nula_deepsleep_esp32s3.upload.maximum_size=1310720 +soldered_nula_deepsleep_esp32s3.upload.maximum_data_size=327680 +soldered_nula_deepsleep_esp32s3.upload.flags= +soldered_nula_deepsleep_esp32s3.upload.extra_flags= +soldered_nula_deepsleep_esp32s3.upload.use_1200bps_touch=false +soldered_nula_deepsleep_esp32s3.upload.wait_for_upload_port=false + +soldered_nula_deepsleep_esp32s3.serial.disableDTR=false +soldered_nula_deepsleep_esp32s3.serial.disableRTS=false + +soldered_nula_deepsleep_esp32s3.build.tarch=xtensa +soldered_nula_deepsleep_esp32s3.build.bootloader_addr=0x0 +soldered_nula_deepsleep_esp32s3.build.target=esp32s3 +soldered_nula_deepsleep_esp32s3.build.mcu=esp32s3 +soldered_nula_deepsleep_esp32s3.build.core=esp32 +soldered_nula_deepsleep_esp32s3.build.variant=soldered_nula_deepsleep_esp32s3 +soldered_nula_deepsleep_esp32s3.build.board=NULA_DEEPSLEEP + +soldered_nula_deepsleep_esp32s3.build.usb_mode=1 +soldered_nula_deepsleep_esp32s3.build.cdc_on_boot=0 +soldered_nula_deepsleep_esp32s3.build.msc_on_boot=0 +soldered_nula_deepsleep_esp32s3.build.dfu_on_boot=0 +soldered_nula_deepsleep_esp32s3.build.f_cpu=240000000L +soldered_nula_deepsleep_esp32s3.build.flash_size=8MB +soldered_nula_deepsleep_esp32s3.build.flash_freq=80m +soldered_nula_deepsleep_esp32s3.build.flash_mode=dio +soldered_nula_deepsleep_esp32s3.build.boot=qio +soldered_nula_deepsleep_esp32s3.build.boot_freq=80m +soldered_nula_deepsleep_esp32s3.build.partitions=default +soldered_nula_deepsleep_esp32s3.build.defines= +soldered_nula_deepsleep_esp32s3.build.loop_core= +soldered_nula_deepsleep_esp32s3.build.event_core= +soldered_nula_deepsleep_esp32s3.build.psram_type=opi +soldered_nula_deepsleep_esp32s3.build.memory_type={build.boot}_{build.psram_type} + +## IDE 2.0 Seems to not update the value +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.default=Disabled +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.default.build.copy_jtag_files=0 +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.builtin=Integrated USB JTAG +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.external=FTDI Adapter +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.external.build.copy_jtag_files=1 +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.bridge=ESP USB Bridge +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +soldered_nula_deepsleep_esp32s3.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +soldered_nula_deepsleep_esp32s3.menu.PSRAM.enabled=OPI PSRAM +soldered_nula_deepsleep_esp32s3.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +soldered_nula_deepsleep_esp32s3.menu.PSRAM.enabled.build.psram_type=opi +soldered_nula_deepsleep_esp32s3.menu.PSRAM.disabled=disabled +soldered_nula_deepsleep_esp32s3.menu.PSRAM.disabled.build.defines= + +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio=QIO 80MHz +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio.build.flash_mode=dio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio.build.boot=qio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio.build.boot_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio.build.flash_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio120=QIO 120MHz +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio120.build.flash_mode=dio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio120.build.boot=qio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio120.build.boot_freq=120m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.qio120.build.flash_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.dio=DIO 80MHz +soldered_nula_deepsleep_esp32s3.menu.FlashMode.dio.build.flash_mode=dio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.dio.build.boot=dio +soldered_nula_deepsleep_esp32s3.menu.FlashMode.dio.build.boot_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.dio.build.flash_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.opi=OPI 80MHz +soldered_nula_deepsleep_esp32s3.menu.FlashMode.opi.build.flash_mode=dout +soldered_nula_deepsleep_esp32s3.menu.FlashMode.opi.build.boot=opi +soldered_nula_deepsleep_esp32s3.menu.FlashMode.opi.build.boot_freq=80m +soldered_nula_deepsleep_esp32s3.menu.FlashMode.opi.build.flash_freq=80m + +soldered_nula_deepsleep_esp32s3.menu.FlashSize.8M=8MB (64Mb) +soldered_nula_deepsleep_esp32s3.menu.FlashSize.8M.build.flash_size=8MB + +soldered_nula_deepsleep_esp32s3.menu.LoopCore.1=Core 1 +soldered_nula_deepsleep_esp32s3.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +soldered_nula_deepsleep_esp32s3.menu.LoopCore.0=Core 0 +soldered_nula_deepsleep_esp32s3.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +soldered_nula_deepsleep_esp32s3.menu.EventsCore.1=Core 1 +soldered_nula_deepsleep_esp32s3.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +soldered_nula_deepsleep_esp32s3.menu.EventsCore.0=Core 0 +soldered_nula_deepsleep_esp32s3.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +soldered_nula_deepsleep_esp32s3.menu.USBMode.hwcdc=Hardware CDC and JTAG +soldered_nula_deepsleep_esp32s3.menu.USBMode.hwcdc.build.usb_mode=1 +soldered_nula_deepsleep_esp32s3.menu.USBMode.default=USB-OTG (TinyUSB) +soldered_nula_deepsleep_esp32s3.menu.USBMode.default.build.usb_mode=0 + +soldered_nula_deepsleep_esp32s3.menu.CDCOnBoot.default=Disabled +soldered_nula_deepsleep_esp32s3.menu.CDCOnBoot.default.build.cdc_on_boot=0 +soldered_nula_deepsleep_esp32s3.menu.CDCOnBoot.cdc=Enabled +soldered_nula_deepsleep_esp32s3.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +soldered_nula_deepsleep_esp32s3.menu.MSCOnBoot.default=Disabled +soldered_nula_deepsleep_esp32s3.menu.MSCOnBoot.default.build.msc_on_boot=0 +soldered_nula_deepsleep_esp32s3.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +soldered_nula_deepsleep_esp32s3.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +soldered_nula_deepsleep_esp32s3.menu.DFUOnBoot.default=Disabled +soldered_nula_deepsleep_esp32s3.menu.DFUOnBoot.default.build.dfu_on_boot=0 +soldered_nula_deepsleep_esp32s3.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +soldered_nula_deepsleep_esp32s3.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +soldered_nula_deepsleep_esp32s3.menu.UploadMode.default=UART0 / Hardware CDC +soldered_nula_deepsleep_esp32s3.menu.UploadMode.default.upload.use_1200bps_touch=false +soldered_nula_deepsleep_esp32s3.menu.UploadMode.default.upload.wait_for_upload_port=false +soldered_nula_deepsleep_esp32s3.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +soldered_nula_deepsleep_esp32s3.menu.UploadMode.cdc.upload.use_1200bps_touch=true +soldered_nula_deepsleep_esp32s3.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.default.build.partitions=default +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.minimal.build.partitions=minimal +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_fs.build.partitions=no_fs +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_ota.build.partitions=no_ota +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.huge_app.build.partitions=huge_app +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker=RainMaker 4MB +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4096000 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.custom=Custom +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.custom.build.partitions= +soldered_nula_deepsleep_esp32s3.menu.PartitionScheme.custom.upload.maximum_size=8388608 + +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.240=240MHz (WiFi) +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.240.build.f_cpu=240000000L +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.160=160MHz (WiFi) +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.160.build.f_cpu=160000000L +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.80=80MHz (WiFi) +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.80.build.f_cpu=80000000L +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.40=40MHz +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.40.build.f_cpu=40000000L +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.20=20MHz +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.20.build.f_cpu=20000000L +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.10=10MHz +soldered_nula_deepsleep_esp32s3.menu.CPUFreq.10.build.f_cpu=10000000L + +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.921600=921600 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.921600.upload.speed=921600 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.115200=115200 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.115200.upload.speed=115200 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.256000.windows=256000 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.256000.upload.speed=256000 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.230400.windows.upload.speed=256000 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.230400=230400 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.230400.upload.speed=230400 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.460800.linux=460800 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.460800.macosx=460800 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.460800.upload.speed=460800 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.512000.windows=512000 +soldered_nula_deepsleep_esp32s3.menu.UploadSpeed.512000.upload.speed=512000 + +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.none=None +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.none.build.code_debug=0 +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.error=Error +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.error.build.code_debug=1 +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.warn=Warn +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.warn.build.code_debug=2 +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.info=Info +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.info.build.code_debug=3 +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.debug=Debug +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.debug.build.code_debug=4 +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.verbose=Verbose +soldered_nula_deepsleep_esp32s3.menu.DebugLevel.verbose.build.code_debug=5 + +soldered_nula_deepsleep_esp32s3.menu.EraseFlash.none=Disabled +soldered_nula_deepsleep_esp32s3.menu.EraseFlash.none.upload.erase_cmd= +soldered_nula_deepsleep_esp32s3.menu.EraseFlash.all=Enabled +soldered_nula_deepsleep_esp32s3.menu.EraseFlash.all.upload.erase_cmd=-e + +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.default=Disabled +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.default.build.zigbee_mode= +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.default.build.zigbee_libs= +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +soldered_nula_deepsleep_esp32s3.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +soldered_nula_mini_esp32c6.name=Soldered NULA Mini ESP32C6 + +soldered_nula_mini_esp32c6.bootloader.tool=esptool_py +soldered_nula_mini_esp32c6.bootloader.tool.default=esptool_py + +soldered_nula_mini_esp32c6.upload.tool=esptool_py +soldered_nula_mini_esp32c6.upload.tool.default=esptool_py +soldered_nula_mini_esp32c6.upload.tool.network=esp_ota + +soldered_nula_mini_esp32c6.upload.maximum_size=1310720 +soldered_nula_mini_esp32c6.upload.maximum_data_size=327680 +soldered_nula_mini_esp32c6.upload.flags= +soldered_nula_mini_esp32c6.upload.extra_flags= +soldered_nula_mini_esp32c6.upload.use_1200bps_touch=false +soldered_nula_mini_esp32c6.upload.wait_for_upload_port=false + +soldered_nula_mini_esp32c6.serial.disableDTR=true +soldered_nula_mini_esp32c6.serial.disableRTS=true + +soldered_nula_mini_esp32c6.build.tarch=riscv32 +soldered_nula_mini_esp32c6.build.target=esp +soldered_nula_mini_esp32c6.build.mcu=esp32c6 +soldered_nula_mini_esp32c6.build.core=esp32 +soldered_nula_mini_esp32c6.build.variant=soldered_nula_mini_esp32c6 +soldered_nula_mini_esp32c6.build.board=SOLDERED_NULA_MINI_ESP32C6 +soldered_nula_mini_esp32c6.build.bootloader_addr=0x0 + +soldered_nula_mini_esp32c6.build.f_cpu=160000000L +soldered_nula_mini_esp32c6.build.flash_size=4MB +soldered_nula_mini_esp32c6.build.flash_freq=80m +soldered_nula_mini_esp32c6.build.flash_mode=qio +soldered_nula_mini_esp32c6.build.boot=qio +soldered_nula_mini_esp32c6.build.partitions=default +soldered_nula_mini_esp32c6.build.defines= + +## IDE 2.0 Seems to not update the value +soldered_nula_mini_esp32c6.menu.JTAGAdapter.default=Disabled +soldered_nula_mini_esp32c6.menu.JTAGAdapter.default.build.copy_jtag_files=0 +soldered_nula_mini_esp32c6.menu.JTAGAdapter.builtin=Integrated USB JTAG +soldered_nula_mini_esp32c6.menu.JTAGAdapter.builtin.build.openocdscript=esp32c6-builtin.cfg +soldered_nula_mini_esp32c6.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +soldered_nula_mini_esp32c6.menu.JTAGAdapter.external=FTDI Adapter +soldered_nula_mini_esp32c6.menu.JTAGAdapter.external.build.openocdscript=esp32c6-ftdi.cfg +soldered_nula_mini_esp32c6.menu.JTAGAdapter.external.build.copy_jtag_files=1 +soldered_nula_mini_esp32c6.menu.JTAGAdapter.bridge=ESP USB Bridge +soldered_nula_mini_esp32c6.menu.JTAGAdapter.bridge.build.openocdscript=esp32c6-bridge.cfg +soldered_nula_mini_esp32c6.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +soldered_nula_mini_esp32c6.menu.CDCOnBoot.default=Disabled +soldered_nula_mini_esp32c6.menu.CDCOnBoot.default.build.cdc_on_boot=0 +soldered_nula_mini_esp32c6.menu.CDCOnBoot.cdc=Enabled +soldered_nula_mini_esp32c6.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +soldered_nula_mini_esp32c6.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.default.build.partitions=default +soldered_nula_mini_esp32c6.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +soldered_nula_mini_esp32c6.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.minimal.build.partitions=minimal +soldered_nula_mini_esp32c6.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.no_ota.build.partitions=no_ota +soldered_nula_mini_esp32c6.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +soldered_nula_mini_esp32c6.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.huge_app.build.partitions=huge_app +soldered_nula_mini_esp32c6.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +soldered_nula_mini_esp32c6.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +soldered_nula_mini_esp32c6.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +soldered_nula_mini_esp32c6.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker=RainMaker 4MB +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +soldered_nula_mini_esp32c6.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee.build.partitions=zigbee +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +soldered_nula_mini_esp32c6.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +soldered_nula_mini_esp32c6.menu.PartitionScheme.custom=Custom +soldered_nula_mini_esp32c6.menu.PartitionScheme.custom.build.partitions= +soldered_nula_mini_esp32c6.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +soldered_nula_mini_esp32c6.menu.CPUFreq.160=160MHz (WiFi) +soldered_nula_mini_esp32c6.menu.CPUFreq.160.build.f_cpu=160000000L +soldered_nula_mini_esp32c6.menu.CPUFreq.80=80MHz (WiFi) +soldered_nula_mini_esp32c6.menu.CPUFreq.80.build.f_cpu=80000000L +soldered_nula_mini_esp32c6.menu.CPUFreq.40=40MHz +soldered_nula_mini_esp32c6.menu.CPUFreq.40.build.f_cpu=40000000L +soldered_nula_mini_esp32c6.menu.CPUFreq.20=20MHz +soldered_nula_mini_esp32c6.menu.CPUFreq.20.build.f_cpu=20000000L +soldered_nula_mini_esp32c6.menu.CPUFreq.10=10MHz +soldered_nula_mini_esp32c6.menu.CPUFreq.10.build.f_cpu=10000000L + +soldered_nula_mini_esp32c6.menu.FlashMode.qio=QIO +soldered_nula_mini_esp32c6.menu.FlashMode.qio.build.flash_mode=dio +soldered_nula_mini_esp32c6.menu.FlashMode.qio.build.boot=qio +soldered_nula_mini_esp32c6.menu.FlashMode.dio=DIO +soldered_nula_mini_esp32c6.menu.FlashMode.dio.build.flash_mode=dio +soldered_nula_mini_esp32c6.menu.FlashMode.dio.build.boot=dio + +soldered_nula_mini_esp32c6.menu.FlashFreq.80=80MHz +soldered_nula_mini_esp32c6.menu.FlashFreq.80.build.flash_freq=80m +soldered_nula_mini_esp32c6.menu.FlashFreq.40=40MHz +soldered_nula_mini_esp32c6.menu.FlashFreq.40.build.flash_freq=40m + +soldered_nula_mini_esp32c6.menu.FlashSize.4M=4MB (32Mb) +soldered_nula_mini_esp32c6.menu.FlashSize.4M.build.flash_size=4MB + +soldered_nula_mini_esp32c6.menu.UploadSpeed.921600=921600 +soldered_nula_mini_esp32c6.menu.UploadSpeed.921600.upload.speed=921600 +soldered_nula_mini_esp32c6.menu.UploadSpeed.115200=115200 +soldered_nula_mini_esp32c6.menu.UploadSpeed.115200.upload.speed=115200 +soldered_nula_mini_esp32c6.menu.UploadSpeed.256000.windows=256000 +soldered_nula_mini_esp32c6.menu.UploadSpeed.256000.upload.speed=256000 +soldered_nula_mini_esp32c6.menu.UploadSpeed.230400.windows.upload.speed=256000 +soldered_nula_mini_esp32c6.menu.UploadSpeed.230400=230400 +soldered_nula_mini_esp32c6.menu.UploadSpeed.230400.upload.speed=230400 +soldered_nula_mini_esp32c6.menu.UploadSpeed.460800.linux=460800 +soldered_nula_mini_esp32c6.menu.UploadSpeed.460800.macosx=460800 +soldered_nula_mini_esp32c6.menu.UploadSpeed.460800.upload.speed=460800 +soldered_nula_mini_esp32c6.menu.UploadSpeed.512000.windows=512000 +soldered_nula_mini_esp32c6.menu.UploadSpeed.512000.upload.speed=512000 + +soldered_nula_mini_esp32c6.menu.DebugLevel.none=None +soldered_nula_mini_esp32c6.menu.DebugLevel.none.build.code_debug=0 +soldered_nula_mini_esp32c6.menu.DebugLevel.error=Error +soldered_nula_mini_esp32c6.menu.DebugLevel.error.build.code_debug=1 +soldered_nula_mini_esp32c6.menu.DebugLevel.warn=Warn +soldered_nula_mini_esp32c6.menu.DebugLevel.warn.build.code_debug=2 +soldered_nula_mini_esp32c6.menu.DebugLevel.info=Info +soldered_nula_mini_esp32c6.menu.DebugLevel.info.build.code_debug=3 +soldered_nula_mini_esp32c6.menu.DebugLevel.debug=Debug +soldered_nula_mini_esp32c6.menu.DebugLevel.debug.build.code_debug=4 +soldered_nula_mini_esp32c6.menu.DebugLevel.verbose=Verbose +soldered_nula_mini_esp32c6.menu.DebugLevel.verbose.build.code_debug=5 + +soldered_nula_mini_esp32c6.menu.EraseFlash.none=Disabled +soldered_nula_mini_esp32c6.menu.EraseFlash.none.upload.erase_cmd= +soldered_nula_mini_esp32c6.menu.EraseFlash.all=Enabled +soldered_nula_mini_esp32c6.menu.EraseFlash.all.upload.erase_cmd=-e + +soldered_nula_mini_esp32c6.menu.ZigbeeMode.default=Disabled +soldered_nula_mini_esp32c6.menu.ZigbeeMode.default.build.zigbee_mode= +soldered_nula_mini_esp32c6.menu.ZigbeeMode.default.build.zigbee_libs= +soldered_nula_mini_esp32c6.menu.ZigbeeMode.ed=Zigbee ED (end device) +soldered_nula_mini_esp32c6.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +soldered_nula_mini_esp32c6.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +soldered_nula_mini_esp32c6.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +soldered_nula_mini_esp32c6.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +soldered_nula_mini_esp32c6.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native + +############################################################## + +arduino_nesso_n1.name=Arduino Nesso N1 + +arduino_nesso_n1.bootloader.tool=esptool_py +arduino_nesso_n1.bootloader.tool.default=esptool_py + +arduino_nesso_n1.upload.tool=esptool_py +arduino_nesso_n1.upload.tool.default=esptool_py +arduino_nesso_n1.upload.tool.network=esp_ota + +arduino_nesso_n1.upload.maximum_size=1310720 +arduino_nesso_n1.upload.maximum_data_size=327680 +arduino_nesso_n1.upload.flags= +arduino_nesso_n1.upload.extra_flags= +arduino_nesso_n1.upload.use_1200bps_touch=false +arduino_nesso_n1.upload.wait_for_upload_port=false + +arduino_nesso_n1.serial.disableDTR=false +arduino_nesso_n1.serial.disableRTS=false + +arduino_nesso_n1.build.tarch=riscv32 +arduino_nesso_n1.build.target=esp +arduino_nesso_n1.build.mcu=esp32c6 +arduino_nesso_n1.build.core=esp32 +arduino_nesso_n1.build.variant=arduino_nesso_n1 +arduino_nesso_n1.build.board=ARDUINO_NESSO_N1 +arduino_nesso_n1.build.bootloader_addr=0x0 + +arduino_nesso_n1.build.cdc_on_boot=1 +arduino_nesso_n1.build.f_cpu=160000000L +arduino_nesso_n1.build.flash_size=16MB +arduino_nesso_n1.build.flash_freq=80m +arduino_nesso_n1.build.flash_mode=qio +arduino_nesso_n1.build.boot=qio +arduino_nesso_n1.build.partitions=default +arduino_nesso_n1.build.defines= + +## IDE 2.0 Seems to not update the value +arduino_nesso_n1.menu.JTAGAdapter.default=Disabled +arduino_nesso_n1.menu.JTAGAdapter.default.build.copy_jtag_files=0 +arduino_nesso_n1.menu.JTAGAdapter.builtin=Integrated USB JTAG +arduino_nesso_n1.menu.JTAGAdapter.builtin.build.openocdscript=esp32c6-builtin.cfg +arduino_nesso_n1.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +arduino_nesso_n1.menu.JTAGAdapter.external=FTDI Adapter +arduino_nesso_n1.menu.JTAGAdapter.external.build.openocdscript=esp32c6-ftdi.cfg +arduino_nesso_n1.menu.JTAGAdapter.external.build.copy_jtag_files=1 +arduino_nesso_n1.menu.JTAGAdapter.bridge=ESP USB Bridge +arduino_nesso_n1.menu.JTAGAdapter.bridge.build.openocdscript=esp32c6-bridge.cfg +arduino_nesso_n1.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +arduino_nesso_n1.menu.CDCOnBoot.default=Enabled +arduino_nesso_n1.menu.CDCOnBoot.default.build.cdc_on_boot=1 +arduino_nesso_n1.menu.CDCOnBoot.cdc=Disabled +arduino_nesso_n1.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +arduino_nesso_n1.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +arduino_nesso_n1.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +arduino_nesso_n1.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 +arduino_nesso_n1.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +arduino_nesso_n1.menu.PartitionScheme.default.build.partitions=default +arduino_nesso_n1.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +arduino_nesso_n1.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +arduino_nesso_n1.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +arduino_nesso_n1.menu.PartitionScheme.no_ota.build.partitions=no_ota +arduino_nesso_n1.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +arduino_nesso_n1.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +arduino_nesso_n1.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +arduino_nesso_n1.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +arduino_nesso_n1.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +arduino_nesso_n1.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +arduino_nesso_n1.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +arduino_nesso_n1.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +arduino_nesso_n1.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +arduino_nesso_n1.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +arduino_nesso_n1.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +arduino_nesso_n1.menu.PartitionScheme.huge_app.build.partitions=huge_app +arduino_nesso_n1.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +arduino_nesso_n1.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +arduino_nesso_n1.menu.PartitionScheme.zigbee.build.partitions=zigbee +arduino_nesso_n1.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +arduino_nesso_n1.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +arduino_nesso_n1.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +arduino_nesso_n1.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 + +arduino_nesso_n1.menu.CPUFreq.160=160MHz (WiFi) +arduino_nesso_n1.menu.CPUFreq.160.build.f_cpu=160000000L +arduino_nesso_n1.menu.CPUFreq.80=80MHz (WiFi) +arduino_nesso_n1.menu.CPUFreq.80.build.f_cpu=80000000L +arduino_nesso_n1.menu.CPUFreq.40=40MHz +arduino_nesso_n1.menu.CPUFreq.40.build.f_cpu=40000000L +arduino_nesso_n1.menu.CPUFreq.20=20MHz +arduino_nesso_n1.menu.CPUFreq.20.build.f_cpu=20000000L +arduino_nesso_n1.menu.CPUFreq.10=10MHz +arduino_nesso_n1.menu.CPUFreq.10.build.f_cpu=10000000L + +arduino_nesso_n1.menu.FlashMode.qio=QIO +arduino_nesso_n1.menu.FlashMode.qio.build.flash_mode=dio +arduino_nesso_n1.menu.FlashMode.qio.build.boot=qio +arduino_nesso_n1.menu.FlashMode.dio=DIO +arduino_nesso_n1.menu.FlashMode.dio.build.flash_mode=dio +arduino_nesso_n1.menu.FlashMode.dio.build.boot=dio + +arduino_nesso_n1.menu.UploadSpeed.921600=921600 +arduino_nesso_n1.menu.UploadSpeed.921600.upload.speed=921600 +arduino_nesso_n1.menu.UploadSpeed.115200=115200 +arduino_nesso_n1.menu.UploadSpeed.115200.upload.speed=115200 +arduino_nesso_n1.menu.UploadSpeed.256000.windows=256000 +arduino_nesso_n1.menu.UploadSpeed.256000.upload.speed=256000 +arduino_nesso_n1.menu.UploadSpeed.230400.windows.upload.speed=256000 +arduino_nesso_n1.menu.UploadSpeed.230400=230400 +arduino_nesso_n1.menu.UploadSpeed.230400.upload.speed=230400 +arduino_nesso_n1.menu.UploadSpeed.460800.linux=460800 +arduino_nesso_n1.menu.UploadSpeed.460800.macosx=460800 +arduino_nesso_n1.menu.UploadSpeed.460800.upload.speed=460800 +arduino_nesso_n1.menu.UploadSpeed.512000.windows=512000 +arduino_nesso_n1.menu.UploadSpeed.512000.upload.speed=512000 + +arduino_nesso_n1.menu.DebugLevel.none=None +arduino_nesso_n1.menu.DebugLevel.none.build.code_debug=0 +arduino_nesso_n1.menu.DebugLevel.error=Error +arduino_nesso_n1.menu.DebugLevel.error.build.code_debug=1 +arduino_nesso_n1.menu.DebugLevel.warn=Warn +arduino_nesso_n1.menu.DebugLevel.warn.build.code_debug=2 +arduino_nesso_n1.menu.DebugLevel.info=Info +arduino_nesso_n1.menu.DebugLevel.info.build.code_debug=3 +arduino_nesso_n1.menu.DebugLevel.debug=Debug +arduino_nesso_n1.menu.DebugLevel.debug.build.code_debug=4 +arduino_nesso_n1.menu.DebugLevel.verbose=Verbose +arduino_nesso_n1.menu.DebugLevel.verbose.build.code_debug=5 + +arduino_nesso_n1.menu.EraseFlash.none=Disabled +arduino_nesso_n1.menu.EraseFlash.none.upload.erase_cmd= +arduino_nesso_n1.menu.EraseFlash.all=Enabled +arduino_nesso_n1.menu.EraseFlash.all.upload.erase_cmd=-e + +arduino_nesso_n1.menu.ZigbeeMode.default=Disabled +arduino_nesso_n1.menu.ZigbeeMode.default.build.zigbee_mode= +arduino_nesso_n1.menu.ZigbeeMode.default.build.zigbee_libs= +arduino_nesso_n1.menu.ZigbeeMode.ed=Zigbee ED (end device) +arduino_nesso_n1.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +arduino_nesso_n1.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +arduino_nesso_n1.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +arduino_nesso_n1.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +arduino_nesso_n1.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native + +############################################################## diff --git a/cores/esp32/Arduino.h b/cores/esp32/Arduino.h index 9048249a873..5a5b0c6814c 100644 --- a/cores/esp32/Arduino.h +++ b/cores/esp32/Arduino.h @@ -222,12 +222,36 @@ size_t getArduinoLoopTaskStackSize(void); return sz; \ } +#define ESP32_USB_MIDI_DEFAULT_NAME "TinyUSB MIDI" +/** +* @brief Set the current device name +* 1. Name set via constructor (if any) +* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) +* 3. Default name "TinyUSB MIDI" +* If device name is set as "", it will be ignored +*/ +#define SET_USB_MIDI_DEVICE_NAME(name) \ + const char *getUSBMIDIDefaultDeviceName() { \ + if (!name || strlen(name) == 0) { \ + return ESP32_USB_MIDI_DEFAULT_NAME; \ + } \ + return name; \ + } + bool shouldPrintChipDebugReport(void); #define ENABLE_CHIP_DEBUG_REPORT \ bool shouldPrintChipDebugReport(void) { \ return true; \ } +// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds +// before the sketch would start its execution. It gives the user time to open the Serial Monitor +uint64_t getArduinoSetupWaitTime_ms(void); +#define SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) \ + uint64_t getArduinoSetupWaitTime_ms() { \ + return (time_ms); \ + } + // allows user to bypass esp_spiram_test() bool esp_psram_extram_test(void); #define BYPASS_SPIRAM_TEST(bypass) \ diff --git a/cores/esp32/Esp.cpp b/cores/esp32/Esp.cpp index f911b2d5f20..023afd80d8b 100644 --- a/cores/esp32/Esp.cpp +++ b/cores/esp32/Esp.cpp @@ -36,6 +36,22 @@ extern "C" { #include "esp_chip_info.h" #include "esp_mac.h" #include "esp_flash.h" + +// Include HAL layer for flash clock access +#include "hal/spi_flash_ll.h" +#if CONFIG_IDF_TARGET_ESP32 +#include "soc/spi_struct.h" +#else +// All modern chips (S2, S3, C2, C3, C5, C6, H2, P4) use spimem +#include "hal/spimem_flash_ll.h" +// Try to include the newer c_struct header first, fall back to regular struct +#if __has_include("soc/spi_mem_c_struct.h") +#include "soc/spi_mem_c_struct.h" +#else +#include "soc/spi_mem_struct.h" +#endif +#endif + #ifdef ESP_IDF_VERSION_MAJOR // IDF 4+ #if CONFIG_IDF_TARGET_ESP32 // ESP32/PICO-D4 #include "esp32/rom/spi_flash.h" @@ -67,6 +83,9 @@ extern "C" { #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/spi_flash.h" #define ESP_FLASH_IMAGE_BASE 0x2000 // Esp32c5 is located at 0x2000 +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/spi_flash.h" +#define ESP_FLASH_IMAGE_BASE 0x0000 // Esp32c61 is located at 0x0000 #else #error Target CONFIG_IDF_TARGET is not supported #endif @@ -91,39 +110,39 @@ extern "C" { * uint32_t = test = 10_MHz; // --> 10000000 */ -unsigned long long operator"" _kHz(unsigned long long x) { +unsigned long long operator""_kHz(unsigned long long x) { return x * 1000; } -unsigned long long operator"" _MHz(unsigned long long x) { +unsigned long long operator""_MHz(unsigned long long x) { return x * 1000 * 1000; } -unsigned long long operator"" _GHz(unsigned long long x) { +unsigned long long operator""_GHz(unsigned long long x) { return x * 1000 * 1000 * 1000; } -unsigned long long operator"" _kBit(unsigned long long x) { +unsigned long long operator""_kBit(unsigned long long x) { return x * 1024; } -unsigned long long operator"" _MBit(unsigned long long x) { +unsigned long long operator""_MBit(unsigned long long x) { return x * 1024 * 1024; } -unsigned long long operator"" _GBit(unsigned long long x) { +unsigned long long operator""_GBit(unsigned long long x) { return x * 1024 * 1024 * 1024; } -unsigned long long operator"" _kB(unsigned long long x) { +unsigned long long operator""_kB(unsigned long long x) { return x * 1024; } -unsigned long long operator"" _MB(unsigned long long x) { +unsigned long long operator""_MB(unsigned long long x) { return x * 1024 * 1024; } -unsigned long long operator"" _GB(unsigned long long x) { +unsigned long long operator""_GB(unsigned long long x) { return x * 1024 * 1024 * 1024; } @@ -348,17 +367,13 @@ uint32_t EspClass::getFlashChipSpeed(void) { return magicFlashChipSpeed(fhdr.spi_speed); } -// FIXME for P4 -#if !defined(CONFIG_IDF_TARGET_ESP32P4) FlashMode_t EspClass::getFlashChipMode(void) { -#if CONFIG_IDF_TARGET_ESP32S2 +#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 uint32_t spi_ctrl = REG_READ(PERIPHS_SPI_FLASH_CTRL); -#else -#if CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 +#elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 uint32_t spi_ctrl = REG_READ(DR_REG_SPI0_BASE + 0x8); #else uint32_t spi_ctrl = REG_READ(SPI_CTRL_REG(0)); -#endif #endif /* Not all of the following constants are already defined in older versions of spi_reg.h, so do it manually for now*/ if (spi_ctrl & BIT(24)) { //SPI_FREAD_QIO @@ -374,11 +389,9 @@ FlashMode_t EspClass::getFlashChipMode(void) { } else { return (FM_SLOW_READ); } - return (FM_DOUT); } -#endif // if !defined(CONFIG_IDF_TARGET_ESP32P4) -uint32_t EspClass::magicFlashChipSize(uint8_t byte) { +uint32_t EspClass::magicFlashChipSize(uint8_t flashByte) { /* FLASH_SIZES = { "1MB": 0x00, @@ -391,7 +404,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { "128MB": 0x70, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0x0: return (1_MB); // 8 MBit (1MB) case 0x1: return (2_MB); // 16 MBit (2MB) case 0x2: return (4_MB); // 32 MBit (4MB) @@ -405,7 +418,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { } } -uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { +uint32_t EspClass::magicFlashChipSpeed(uint8_t flashByte) { #if CONFIG_IDF_TARGET_ESP32C2 /* FLASH_FREQUENCY = { @@ -415,7 +428,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "15m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (60_MHz); case 0x0: return (30_MHz); case 0x1: return (20_MHz); @@ -432,13 +445,29 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "20m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0x0: return (80_MHz); case 0x2: return (20_MHz); default: // fail? return 0; } +#elif CONFIG_IDF_TARGET_ESP32C61 + /* + FLASH_FREQUENCY = { + "80m": 0xF, + "40m": 0x0, + "20m": 0x2, + } +*/ + switch (flashByte & 0x0F) { + case 0xF: return (80_MHz); + case 0x0: return (40_MHz); + case 0x2: return (20_MHz); + default: // fail? + return 0; + } + #elif CONFIG_IDF_TARGET_ESP32H2 /* @@ -449,7 +478,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "12m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (48_MHz); case 0x0: return (24_MHz); case 0x1: return (16_MHz); @@ -467,7 +496,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "20m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (80_MHz); case 0x0: return (40_MHz); case 0x1: return (26_MHz); @@ -478,8 +507,8 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { #endif } -FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { - FlashMode_t mode = (FlashMode_t)byte; +FlashMode_t EspClass::magicFlashChipMode(uint8_t flashByte) { + FlashMode_t mode = (FlashMode_t)flashByte; if (mode > FM_SLOW_READ) { mode = FM_UNKNOWN; } @@ -516,3 +545,63 @@ uint64_t EspClass::getEfuseMac(void) { esp_efuse_mac_get_default((uint8_t *)(&_chipmacid)); return _chipmacid; } + +// ============================================================================ +// Flash Frequency Runtime Detection +// ============================================================================ + +/** + * @brief Read the source clock frequency using ESP-IDF HAL functions + * @return Source clock frequency in MHz (80, 120, 160, or 240) + */ +uint8_t EspClass::getFlashSourceFrequencyMHz(void) { +#if CONFIG_IDF_TARGET_ESP32 + // ESP32: Use HAL function + return spi_flash_ll_get_source_clock_freq_mhz(0); // host_id = 0 for SPI0 +#else + // All modern MCUs: Use spimem HAL function + return spimem_flash_ll_get_source_freq_mhz(); +#endif +} + +/** + * @brief Read the clock divider from hardware using HAL structures + * Based on ESP-IDF HAL implementation: + * - ESP32: Uses SPI1.clock (typedef in spi_flash_ll.h) + * - All newer MCUs: Use SPIMEM1.clock (typedef in spimem_flash_ll.h) + * @return Clock divider value (1 = no division, 2 = divide by 2, etc.) + */ +uint8_t EspClass::getFlashClockDivider(void) { +#if CONFIG_IDF_TARGET_ESP32 + // ESP32: Flash uses SPI1 + // See: line 52: esp-idf/components/hal/esp32/include/hal/spi_flash_ll.h + if (SPI1.clock.clk_equ_sysclk) { + return 1; // 1:1 clock + } + return SPI1.clock.clkcnt_n + 1; +#else + // All newer MCUs: Flash uses SPIMEM1 + // See: esp-idf/components/hal/esp32*/include/hal/spimem_flash_ll.h + // Example S3: line 38: typedef typeof(SPIMEM1.clock.val) spimem_flash_ll_clock_reg_t; + // Example C5: lines 97-99: esp-idf/components/soc/esp32c5/mp/include/soc/spi_mem_struct.h + if (SPIMEM1.clock.clk_equ_sysclk) { + return 1; // 1:1 clock + } + return SPIMEM1.clock.clkcnt_n + 1; +#endif +} + +/** + * @brief Get the actual flash frequency in MHz + * @return Flash frequency in MHz (80, 120, 160, or 240) + */ +uint32_t EspClass::getFlashFrequencyMHz(void) { + uint8_t source = getFlashSourceFrequencyMHz(); + uint8_t divider = getFlashClockDivider(); + + if (divider == 0) { + divider = 1; // Safety check + } + + return source / divider; +} diff --git a/cores/esp32/Esp.h b/cores/esp32/Esp.h index 41068fb9e65..7d5266f7b1a 100644 --- a/cores/esp32/Esp.h +++ b/cores/esp32/Esp.h @@ -92,9 +92,14 @@ class EspClass { uint32_t getFlashChipSpeed(); FlashMode_t getFlashChipMode(); - uint32_t magicFlashChipSize(uint8_t byte); - uint32_t magicFlashChipSpeed(uint8_t byte); - FlashMode_t magicFlashChipMode(uint8_t byte); + // Flash frequency runtime detection + uint32_t getFlashFrequencyMHz(); + uint8_t getFlashSourceFrequencyMHz(); + uint8_t getFlashClockDivider(); + + uint32_t magicFlashChipSize(uint8_t flashByte); + uint32_t magicFlashChipSpeed(uint8_t flashByte); + FlashMode_t magicFlashChipMode(uint8_t flashByte); uint32_t getSketchSize(); String getSketchMD5(); diff --git a/cores/esp32/HEXBuilder.cpp b/cores/esp32/HEXBuilder.cpp index 6154f58b384..52c53feafa8 100644 --- a/cores/esp32/HEXBuilder.cpp +++ b/cores/esp32/HEXBuilder.cpp @@ -17,8 +17,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include +#include "HEXBuilder.h" +#include static uint8_t hex_char_to_byte(uint8_t c) { return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) @@ -27,6 +27,19 @@ static uint8_t hex_char_to_byte(uint8_t c) { : 0x10; // unknown char is 16 } +bool HEXBuilder::isHexString(const char *str, size_t len) { + for (size_t i = 0; i < len; i++) { + if (isxdigit(str[i]) == 0) { + return false; + } + } + return true; +} + +bool HEXBuilder::isHexString(String str) { + return isHexString(str.c_str(), str.length()); +} + size_t HEXBuilder::hex2bytes(unsigned char *out, size_t maxlen, String &in) { return hex2bytes(out, maxlen, in.c_str()); } diff --git a/cores/esp32/HEXBuilder.h b/cores/esp32/HEXBuilder.h index 0c35fbc1acc..dc977c938c3 100644 --- a/cores/esp32/HEXBuilder.h +++ b/cores/esp32/HEXBuilder.h @@ -23,6 +23,8 @@ #include #include +// Basic hex/byte conversion class to be used by hash builders + class HEXBuilder { public: static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in); @@ -30,5 +32,8 @@ class HEXBuilder { static String bytes2hex(const unsigned char *in, size_t len); static size_t bytes2hex(char *out, size_t maxlen, const unsigned char *in, size_t len); + + static bool isHexString(const char *str, size_t len); + static bool isHexString(String str); }; #endif diff --git a/cores/esp32/HWCDC.cpp b/cores/esp32/HWCDC.cpp index 062317d9f53..87df44b5247 100644 --- a/cores/esp32/HWCDC.cpp +++ b/cores/esp32/HWCDC.cpp @@ -14,6 +14,7 @@ #include "USB.h" #if SOC_USB_SERIAL_JTAG_SUPPORTED +#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY #include "esp32-hal.h" #include "esp32-hal-periman.h" #include "HWCDC.h" @@ -60,7 +61,11 @@ static esp_err_t arduino_hw_cdc_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg) { if (!arduino_hw_cdc_event_loop_handle) { esp_event_loop_args_t event_task_args = { - .queue_size = 5, .task_name = "arduino_hw_cdc_events", .task_priority = 5, .task_stack_size = 2048, .task_core_id = tskNO_AFFINITY + .queue_size = 5, + .task_name = "arduino_hw_cdc_events", + .task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY, + .task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, + .task_core_id = tskNO_AFFINITY }; if (esp_event_loop_create(&event_task_args, &arduino_hw_cdc_event_loop_handle) != ESP_OK) { log_e("esp_event_loop_create failed"); @@ -253,8 +258,6 @@ static void ARDUINO_ISR_ATTR cdc0_write_char(char c) { } HWCDC::HWCDC() { - perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, HWCDC::deinit); - perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, HWCDC::deinit); // SOF in ISR causes problems for uploading firmware // lastSOF_ms = 0; // SOF_TIMEOUT = 5; @@ -324,14 +327,19 @@ void HWCDC::begin(unsigned long baud) { // Peripheral Manager setting for USB D+ D- pins uint8_t pin = USB_INT_PHY0_DM_GPIO_NUM; + if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DM) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DM, HWCDC::deinit); + } if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DM, (void *)this, -1, -1)) { goto err; } pin = USB_INT_PHY0_DP_GPIO_NUM; + if (perimanGetBusDeinit(ESP32_BUS_TYPE_USB_DP) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_USB_DP, HWCDC::deinit); + } if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_USB_DP, (void *)this, -1, -1)) { goto err; } - // Configure PHY // USB_Serial_JTAG use internal PHY USB_SERIAL_JTAG.conf0.phy_sel = 0; diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index 6d762da21fb..7d4e4888e47 100644 --- a/cores/esp32/HardwareSerial.cpp +++ b/cores/esp32/HardwareSerial.cpp @@ -61,6 +61,31 @@ HardwareSerial Serial5(5); extern void HWCDCSerialEvent(void) __attribute__((weak)); #endif +// C-callable helper used by HAL when pins are detached and the high-level +// HardwareSerial instance must be finalized. +extern "C" void hal_uart_notify_pins_detached(int uart_num) { + log_d("hal_uart_notify_pins_detached: Notifying HardwareSerial for UART%d", uart_num); + switch (uart_num) { + case 0: Serial0.end(); break; +#if SOC_UART_NUM > 1 + case 1: Serial1.end(); break; +#endif +#if SOC_UART_NUM > 2 + case 2: Serial2.end(); break; +#endif +#if SOC_UART_NUM > 3 + case 3: Serial3.end(); break; +#endif +#if SOC_UART_NUM > 4 + case 4: Serial4.end(); break; +#endif +#if SOC_UART_NUM > 5 + case 5: Serial5.end(); break; +#endif + default: log_e("hal_uart_notify_pins_detached: UART%d not handled!", uart_num); break; + } +} + #if USB_SERIAL_IS_DEFINED == 1 // Native USB CDC Event // Used by Hardware Serial for USB CDC events extern void USBSerialEvent(void) __attribute__((weak)); @@ -136,8 +161,6 @@ HardwareSerial::HardwareSerial(uint8_t uart_nr) } } #endif - // set deinit function in the Peripheral Manager - uart_init_PeriMan(); } HardwareSerial::~HardwareSerial() { @@ -485,9 +508,6 @@ void HardwareSerial::end() { // including any tasks or debug message channel (log_x()) - but not for IDF log messages! _onReceiveCB = NULL; _onReceiveErrorCB = NULL; - if (uartGetDebug() == _uart_nr) { - uartSetDebug(0); - } _rxFIFOFull = 0; uartEnd(_uart_nr); // fully detach all pins and delete the UART driver _destroyEventTask(); // when IDF uart driver is deleted, _eventTask must finish too @@ -574,8 +594,20 @@ HardwareSerial::operator bool() const { return uartIsDriverInstalled(_uart); } -void HardwareSerial::setRxInvert(bool invert) { - uartSetRxInvert(_uart, invert); +bool HardwareSerial::setRxInvert(bool invert) { + return uartSetRxInvert(_uart, invert); +} + +bool HardwareSerial::setTxInvert(bool invert) { + return uartSetTxInvert(_uart, invert); +} + +bool HardwareSerial::setCtsInvert(bool invert) { + return uartSetCtsInvert(_uart, invert); +} + +bool HardwareSerial::setRtsInvert(bool invert) { + return uartSetRtsInvert(_uart, invert); } // negative Pin value will keep it unmodified diff --git a/cores/esp32/HardwareSerial.h b/cores/esp32/HardwareSerial.h index bd24e0eec0e..7f843fef4c8 100644 --- a/cores/esp32/HardwareSerial.h +++ b/cores/esp32/HardwareSerial.h @@ -164,6 +164,8 @@ typedef enum { #define SOC_RX0 (gpio_num_t)38 #elif CONFIG_IDF_TARGET_ESP32C5 #define SOC_RX0 (gpio_num_t)12 +#elif CONFIG_IDF_TARGET_ESP32C61 +#define SOC_RX0 (gpio_num_t)10 #endif #endif @@ -182,7 +184,7 @@ typedef enum { #define SOC_TX0 (gpio_num_t)24 #elif CONFIG_IDF_TARGET_ESP32P4 #define SOC_TX0 (gpio_num_t)37 -#elif CONFIG_IDF_TARGET_ESP32C5 +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 #define SOC_TX0 (gpio_num_t)11 #endif #endif @@ -209,6 +211,8 @@ typedef enum { #define RX1 (gpio_num_t)11 #elif CONFIG_IDF_TARGET_ESP32C5 #define RX1 (gpio_num_t)4 +#elif CONFIG_IDF_TARGET_ESP32C61 +#define RX1 (gpio_num_t)8 #endif #endif @@ -231,6 +235,8 @@ typedef enum { #define TX1 (gpio_num_t)10 #elif CONFIG_IDF_TARGET_ESP32C5 #define TX1 (gpio_num_t)5 +#elif CONFIG_IDF_TARGET_ESP32C61 +#define TX1 (gpio_num_t)29 #endif #endif #endif /* SOC_UART_HP_NUM > 1 */ @@ -356,7 +362,12 @@ class HardwareSerial : public Stream { void setDebugOutput(bool); - void setRxInvert(bool); + // functions used to enable or disable UART pins signal inversion + // returns the requested operation success status + bool setRxInvert(bool); + bool setTxInvert(bool); + bool setCtsInvert(bool); + bool setRtsInvert(bool); // Negative Pin Number will keep it unmodified, thus this function can set individual pins // setPins() can be called after or before begin() diff --git a/cores/esp32/HashBuilder.cpp b/cores/esp32/HashBuilder.cpp new file mode 100644 index 00000000000..be3f67e2f78 --- /dev/null +++ b/cores/esp32/HashBuilder.cpp @@ -0,0 +1,38 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "HashBuilder.h" + +void HashBuilder::add(const char *data) { + add((const uint8_t *)data, strlen(data)); +} + +void HashBuilder::add(String data) { + add(data.c_str()); +} + +void HashBuilder::addHexString(const char *data) { + size_t len = strlen(data); + uint8_t *tmp = (uint8_t *)malloc(len / 2); + if (tmp == NULL) { + return; + } + hex2bytes(tmp, len / 2, data); + add(tmp, len / 2); + free(tmp); +} + +void HashBuilder::addHexString(String data) { + addHexString(data.c_str()); +} diff --git a/cores/esp32/HashBuilder.h b/cores/esp32/HashBuilder.h index 77d1c71dbde..dc624043427 100644 --- a/cores/esp32/HashBuilder.h +++ b/cores/esp32/HashBuilder.h @@ -20,29 +20,44 @@ #include "HEXBuilder.h" +/* Try to prevent most compilers from optimizing out clearing of memory that + * becomes unaccessible after this function is called. This is mostly the case + * for clearing local stack variables at the end of a function. This is not + * exactly perfect, i.e., someone could come up with a compiler that figures out + * the pointer is pointing to memset and then end up optimizing the call out, so + * try go a bit further by storing the first octet (now zero) to make this even + * a bit more difficult to optimize out. Once memset_s() is available, that + * could be used here instead. */ +static void *(*const volatile memset_func)(void *, int, size_t) = memset; +static uint8_t forced_memzero_val; + +static inline void forced_memzero(void *ptr, size_t len) { + memset_func(ptr, 0, len); + if (len) { + forced_memzero_val = ((uint8_t *)ptr)[0]; + } +} + +// Base class for hash builders + class HashBuilder : public HEXBuilder { public: virtual ~HashBuilder() {} virtual void begin() = 0; virtual void add(const uint8_t *data, size_t len) = 0; - virtual void add(const char *data) { - add((const uint8_t *)data, strlen(data)); - } - virtual void add(String data) { - add(data.c_str()); - } + void add(const char *data); + void add(String data); - virtual void addHexString(const char *data) = 0; - virtual void addHexString(String data) { - addHexString(data.c_str()); - } + void addHexString(const char *data); + void addHexString(String data); virtual bool addStream(Stream &stream, const size_t maxLen) = 0; virtual void calculate() = 0; virtual void getBytes(uint8_t *output) = 0; virtual void getChars(char *output) = 0; virtual String toString() = 0; + virtual size_t getHashSize() const = 0; }; #endif diff --git a/cores/esp32/MD5Builder.cpp b/cores/esp32/MD5Builder.cpp index cd8aa31b6cc..3c578491c13 100644 --- a/cores/esp32/MD5Builder.cpp +++ b/cores/esp32/MD5Builder.cpp @@ -17,9 +17,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include +#include "HEXBuilder.h" +#include "MD5Builder.h" void MD5Builder::begin(void) { memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN); @@ -30,17 +29,6 @@ void MD5Builder::add(const uint8_t *data, size_t len) { esp_rom_md5_update(&_ctx, data, len); } -void MD5Builder::addHexString(const char *data) { - size_t len = strlen(data); - uint8_t *tmp = (uint8_t *)malloc(len / 2); - if (tmp == NULL) { - return; - } - hex2bytes(tmp, len / 2, data); - add(tmp, len / 2); - free(tmp); -} - bool MD5Builder::addStream(Stream &stream, const size_t maxLen) { const int buf_size = 512; int maxLengthLeft = maxLen; diff --git a/cores/esp32/MD5Builder.h b/cores/esp32/MD5Builder.h index 5728bd3bac0..cbddf8f60e2 100644 --- a/cores/esp32/MD5Builder.h +++ b/cores/esp32/MD5Builder.h @@ -35,19 +35,18 @@ class MD5Builder : public HashBuilder { uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN]; public: - void begin(void) override; - using HashBuilder::add; - void add(const uint8_t *data, size_t len) override; - - using HashBuilder::addHexString; - void addHexString(const char *data) override; + void begin(void) override; + void add(const uint8_t *data, size_t len) override; bool addStream(Stream &stream, const size_t maxLen) override; void calculate(void) override; void getBytes(uint8_t *output) override; void getChars(char *output) override; String toString(void) override; + size_t getHashSize() const override { + return ESP_ROM_MD5_DIGEST_LEN; + } }; #endif diff --git a/cores/esp32/USB.h b/cores/esp32/USB.h index 6d284937e00..782121e6d50 100644 --- a/cores/esp32/USB.h +++ b/cores/esp32/USB.h @@ -21,6 +21,7 @@ #include "esp_event.h" #include "USBCDC.h" +#include "Arduino.h" // defines ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE and ARDUINO_SERIAL_EVENT_TASK_PRIORITY #define ARDUINO_USB_ON_BOOT (ARDUINO_USB_CDC_ON_BOOT | ARDUINO_USB_MSC_ON_BOOT | ARDUINO_USB_DFU_ON_BOOT) @@ -43,7 +44,7 @@ typedef union { class ESPUSB { public: - ESPUSB(size_t event_task_stack_size = 2048, uint8_t event_task_priority = 5); + ESPUSB(size_t event_task_stack_size = ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE, uint8_t event_task_priority = ARDUINO_SERIAL_EVENT_TASK_PRIORITY); ~ESPUSB(); void onEvent(esp_event_handler_t callback); diff --git a/cores/esp32/USBCDC.cpp b/cores/esp32/USBCDC.cpp index c7bb4582d4f..4eea8845b35 100644 --- a/cores/esp32/USBCDC.cpp +++ b/cores/esp32/USBCDC.cpp @@ -405,8 +405,11 @@ size_t USBCDC::write(const uint8_t *buffer, size_t size) { return 0; } size_t to_send = size, so_far = 0; + // writeTimeout will prevent that TinyUSB failure locks the while(to_send) loop + uint32_t writeTimeout = millis() + tx_timeout_ms; while (to_send) { - if (!tud_cdc_n_connected(itf)) { + if (!tud_cdc_n_connected(itf) || (int32_t)(millis() - writeTimeout) >= 0) { + log_e("USB is disconnected or CDC writing has timed out."); size = so_far; break; } diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 6632aa5f85d..7210dfbfbc4 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -910,7 +910,7 @@ long String::toInt(void) const { float String::toFloat(void) const { if (buffer()) { - return atof(buffer()); + return static_cast(atof(buffer())); } return 0; } diff --git a/cores/esp32/chip-debug-report.cpp b/cores/esp32/chip-debug-report.cpp index 8592031ee3f..414dd108a63 100644 --- a/cores/esp32/chip-debug-report.cpp +++ b/cores/esp32/chip-debug-report.cpp @@ -9,6 +9,7 @@ #include "soc/efuse_reg.h" #include "soc/rtc.h" #include "soc/spi_reg.h" +#include "soc/soc.h" #if CONFIG_IDF_TARGET_ESP32S2 #include "esp32s2/rom/spi_flash.h" #endif @@ -16,6 +17,7 @@ #include "Arduino.h" #include "esp32-hal-periman.h" +#include "chip-debug-report.h" #define chip_report_printf log_printf @@ -67,9 +69,8 @@ static void printPkgVersion(void) { #elif CONFIG_IDF_TARGET_ESP32P4 uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS_2_REG, EFUSE_PKG_VERSION); chip_report_printf("%lu", pkg_ver); -#elif CONFIG_IDF_TARGET_ESP32C5 - // ToDo: Update this line when EFUSE_PKG_VERSION is available again for ESP32-C5 - uint32_t pkg_ver = 0; //REG_GET_FIELD(EFUSE_RD_MAC_SYS2_REG, EFUSE_PKG_VERSION); +#elif CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 + uint32_t pkg_ver = REG_GET_FIELD(EFUSE_RD_MAC_SYS2_REG, EFUSE_PKG_VERSION); chip_report_printf("%lu", pkg_ver); #else chip_report_printf("Unknown"); @@ -138,25 +139,15 @@ static void printFlashInfo(void) { chip_report_printf(" Block Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.block_size, b2kb(g_rom_flashchip.block_size)); chip_report_printf(" Sector Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.sector_size, b2kb(g_rom_flashchip.sector_size)); chip_report_printf(" Page Size : %8lu B (%6.1f KB)\n", g_rom_flashchip.page_size, b2kb(g_rom_flashchip.page_size)); - esp_image_header_t fhdr; - esp_flash_read(esp_flash_default_chip, (void *)&fhdr, ESP_FLASH_IMAGE_BASE, sizeof(esp_image_header_t)); - if (fhdr.magic == ESP_IMAGE_HEADER_MAGIC) { - uint32_t f_freq = 0; - switch (fhdr.spi_speed) { -#if CONFIG_IDF_TARGET_ESP32H2 - case 0x0: f_freq = 32; break; - case 0x2: f_freq = 16; break; - case 0xf: f_freq = 64; break; -#else - case 0x0: f_freq = 40; break; - case 0x1: f_freq = 26; break; - case 0x2: f_freq = 20; break; - case 0xf: f_freq = 80; break; -#endif - default: f_freq = fhdr.spi_speed; break; - } - chip_report_printf(" Bus Speed : %lu MHz\n", f_freq); - } + + // Runtime flash frequency detection from hardware registers + uint32_t actual_freq = ESP.getFlashFrequencyMHz(); + uint8_t source_freq = ESP.getFlashSourceFrequencyMHz(); + uint8_t divider = ESP.getFlashClockDivider(); + + chip_report_printf(" Bus Speed : %lu MHz\n", actual_freq); + chip_report_printf(" Flash Frequency : %lu MHz (source: %u MHz, divider: %u)\n", actual_freq, source_freq, divider); + chip_report_printf(" Bus Mode : "); #if CONFIG_ESPTOOLPY_OCT_FLASH chip_report_printf("OPI\n"); @@ -300,7 +291,7 @@ static void printPerimanInfo(void) { void printBeforeSetupInfo(void) { #if ARDUINO_USB_CDC_ON_BOOT - Serial.begin(0); + Serial.begin(); Serial.setDebugOutput(true); uint8_t t = 0; while (!Serial && (t++ < 200)) { diff --git a/cores/esp32/esp32-hal-adc.c b/cores/esp32/esp32-hal-adc.c index c7cc1f5d556..342ce9aefb0 100644 --- a/cores/esp32/esp32-hal-adc.c +++ b/cores/esp32/esp32-hal-adc.c @@ -21,6 +21,35 @@ #include "esp_adc/adc_continuous.h" #include "esp_adc/adc_cali_scheme.h" +#if CONFIG_IDF_TARGET_ESP32P4 && CONFIG_ESP32P4_REV_MIN_FULL >= 300 +// NOTE: These weak definitions allow successful linkage if the real efuse calibration functions are missing. +// This is a workaround for the ESP32P4 rev 3.0+, which is missing efuse calibration functions in the IDF. +__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_ver(void) { + return 0; +} + +__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_init_code(uint32_t atten, uint32_t *code) { + if (code) { + *code = 0; + } + return 0; // 0 means success in ESP-IDF conventions +} + +__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_chan_compens(uint32_t atten, uint32_t *comp) { + if (comp) { + *comp = 0; + } + return 0; +} + +__attribute__((weak)) uint32_t esp_efuse_rtc_calib_get_cal_voltage(uint32_t atten, uint32_t *voltage) { + if (voltage) { + *voltage = 0; + } + return 0; +} +#endif + // ESP32-C2 does not define those two for some reason #ifndef SOC_ADC_DIGI_RESULT_BYTES #define SOC_ADC_DIGI_RESULT_BYTES (4) @@ -75,11 +104,14 @@ static bool adcDetachBus(void *pin) { if (err != ESP_OK) { return false; } -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle); if (err != ESP_OK) { return false; } +#else + log_e("ADC Calibration scheme is not supported!"); + return false; #endif } adc_handle[adc_unit].adc_cali_handle = NULL; @@ -127,7 +159,7 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i log_e("adc_cali_create_scheme_curve_fitting failed with error: %d", err); return err; } -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED log_d("Deleting ADC_UNIT_%d line cali handle", adc_unit); err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle); if (err != ESP_OK) { @@ -145,6 +177,9 @@ esp_err_t __analogChannelConfig(adc_bitwidth_t width, adc_attenuation_t atten, i log_e("adc_cali_create_scheme_line_fitting failed with error: %d", err); return err; } +#else + log_e("ADC Calibration scheme is not supported!"); + return ESP_ERR_NOT_SUPPORTED; #endif } } @@ -310,13 +345,16 @@ uint32_t __analogReadMilliVolts(uint8_t pin) { .bitwidth = __analogWidth, }; err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle); -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED adc_cali_line_fitting_config_t cali_config = { .unit_id = adc_unit, .bitwidth = __analogWidth, .atten = __analogAttenuation, }; err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle); +#else + log_e("ADC Calibration scheme is not supported!"); + return value; #endif if (err != ESP_OK) { log_e("adc_cali_create_scheme_x failed!"); @@ -360,7 +398,7 @@ static uint8_t __adcContinuousAtten = ADC_11db; static uint8_t __adcContinuousWidth = SOC_ADC_DIGI_MAX_BITWIDTH; static uint8_t used_adc_channels = 0; -adc_continuous_data_t *adc_result = NULL; +adc_continuous_result_t *adc_result = NULL; static bool adcContinuousDetachBus(void *adc_unit_number) { adc_unit_t adc_unit = (adc_unit_t)adc_unit_number - 1; @@ -379,11 +417,14 @@ static bool adcContinuousDetachBus(void *adc_unit_number) { if (err != ESP_OK) { return false; } -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle); if (err != ESP_OK) { return false; } +#else + log_e("ADC Calibration scheme is not supported!"); + return false; #endif } adc_handle[adc_unit].adc_cali_handle = NULL; @@ -536,7 +577,7 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi } //Allocate and prepare result structure for adc readings - adc_result = malloc(pins_count * sizeof(adc_continuous_data_t)); + adc_result = malloc(pins_count * sizeof(adc_continuous_result_t)); for (int k = 0; k < pins_count; k++) { adc_result[k].pin = pins[k]; adc_result[k].channel = channel[k]; @@ -552,13 +593,16 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi .bitwidth = __adcContinuousWidth, }; err = adc_cali_create_scheme_curve_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle); -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) //ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED +#elif ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED adc_cali_line_fitting_config_t cali_config = { .unit_id = adc_unit, .bitwidth = __adcContinuousWidth, .atten = __adcContinuousAtten, }; err = adc_cali_create_scheme_line_fitting(&cali_config, &adc_handle[adc_unit].adc_cali_handle); +#else + log_e("ADC Calibration scheme is not supported!"); + return false; #endif if (err != ESP_OK) { log_e("adc_cali_create_scheme_x failed!"); @@ -577,7 +621,7 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi return true; } -bool analogContinuousRead(adc_continuous_data_t **buffer, uint32_t timeout_ms) { +bool analogContinuousRead(adc_continuous_result_t **buffer, uint32_t timeout_ms) { if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) { uint32_t bytes_read = 0; uint32_t read_raw[used_adc_channels]; diff --git a/cores/esp32/esp32-hal-adc.h b/cores/esp32/esp32-hal-adc.h index 6ab5c920cfc..d22ac65d06f 100644 --- a/cores/esp32/esp32-hal-adc.h +++ b/cores/esp32/esp32-hal-adc.h @@ -86,7 +86,7 @@ typedef struct { uint8_t channel; /*! RTC_XTAL_FREQ_AUTO) { - if (xtal < RTC_XTAL_FREQ_40M) { - if (cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal / 2)) { - log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2); - return false; - } - } else if (cpu_freq_mhz <= xtal && cpu_freq_mhz != xtal && cpu_freq_mhz != (xtal / 2) && cpu_freq_mhz != (xtal / 4)) { - log_e("Bad frequency: %u MHz! Options are: 240, 160, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2, xtal / 4); - return false; - } - } + if (!REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) || !REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) { + pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 80"); + } else #endif -#if (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32C5)) - if (cpu_freq_mhz > xtal && cpu_freq_mhz != 240 && cpu_freq_mhz != 160 && cpu_freq_mhz != 120 && cpu_freq_mhz != 80) { - if (xtal >= RTC_XTAL_FREQ_40M) { - log_e("Bad frequency: %u MHz! Options are: 240, 160, 120, 80, %u, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2, xtal / 4); - } else { - log_e("Bad frequency: %u MHz! Options are: 240, 160, 120, 80, %u and %u MHz", cpu_freq_mhz, xtal, xtal / 2); - } - return false; + { + pos += snprintf(supported_frequencies + pos, 256 - pos, "240, 160, 80"); } +#elif TARGET_CPU_FREQ_MAX_160 + pos += snprintf(supported_frequencies + pos, 256 - pos, "160, 120, 80"); +#elif TARGET_CPU_FREQ_MAX_120 + pos += snprintf(supported_frequencies + pos, 256 - pos, "120, 80"); +#elif TARGET_CPU_FREQ_MAX_96 + pos += snprintf(supported_frequencies + pos, 256 - pos, "96, 64, 48"); +#else + free(supported_frequencies); + return "Unknown"; #endif + + // Append xtal and its dividers only if xtal is nonzero + if (xtal != 0) { + // We'll show as: , , [, ] MHz + pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u, %u", xtal, xtal / 2); + #if CONFIG_IDF_TARGET_ESP32 - //check if cpu supports the frequency - if (cpu_freq_mhz == 240) { - //Check if ESP32 is rated for a CPU frequency of 160MHz only - if (REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_RATED) && REG_GET_BIT(EFUSE_BLK0_RDATA3_REG, EFUSE_RD_CHIP_CPU_FREQ_LOW)) { - log_e("Can not switch to 240 MHz! Chip CPU frequency rated for 160MHz."); - cpu_freq_mhz = 160; + // Only append xtal/4 if it's > 0 and meaningful for higher-frequency chips (e.g., ESP32 40MHz/4=10) + if (xtal >= RTC_XTAL_FREQ_40M) { + pos += snprintf(supported_frequencies + pos, 256 - pos, ", %u", xtal / 4); } +#endif } + + pos += snprintf(supported_frequencies + pos, 256 - pos, " MHz"); + return supported_frequencies; +} + +bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) { + rtc_cpu_freq_config_t conf, cconf; + uint32_t capb, apb; + [[maybe_unused]] + uint8_t xtal = 0; + + // ===== Get XTAL Frequency and validate input ===== +#if TARGET_HAS_XTAL_FREQ + xtal = (uint8_t)rtc_clk_xtal_freq_get(); #endif - //Get current CPU clock configuration + + // ===== Get current configuration and check if change is needed ===== rtc_clk_cpu_freq_get_config(&cconf); - //return if frequency has not changed if (cconf.freq_mhz == cpu_freq_mhz) { - return true; + return true; // Frequency already set } - //Get configuration for the new CPU frequency + + // ===== Get configuration for new frequency ===== if (!rtc_clk_cpu_freq_mhz_to_config(cpu_freq_mhz, &conf)) { - log_e("CPU clock could not be set to %u MHz", cpu_freq_mhz); + log_e("CPU clock could not be set to %u MHz. Supported frequencies: %s", cpu_freq_mhz, getSupportedCpuFrequencyMhz(xtal)); return false; } - //Current APB + + // ===== Calculate APB frequencies ===== capb = calculateApb(&cconf); - //New APB apb = calculateApb(&conf); - //Call peripheral functions before the APB change + // ===== Apply frequency change ===== if (apb_change_callbacks) { triggerApbChangeCallback(APB_BEFORE_CHANGE, capb, apb); } - //Make the frequency change + rtc_clk_cpu_freq_set_config_fast(&conf); -#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3) + + // Update APB frequency for targets with dynamic APB +#if TARGET_HAS_DYNAMIC_APB if (capb != apb) { - //Update REF_TICK (uncomment if REF_TICK is different than 1MHz) - //if(conf.freq_mhz < 80){ - // ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1; + // Update REF_TICK (uncomment if REF_TICK is different than 1MHz) + // if (conf.freq_mhz < 80) { + // ESP_REG(APB_CTRL_XTAL_TICK_CONF_REG) = conf.freq_mhz / (REF_CLK_FREQ / MHZ) - 1; // } - //Update APB Freq REG rtc_clk_apb_freq_update(apb); - //Update esp_timer divisor + + // ESP32-specific: Update esp_timer divisor #if CONFIG_IDF_TARGET_ESP32 #if defined(LACT_MODULE) && defined(LACT_TICKS_PER_US) timer_ll_set_lact_clock_prescale(TIMER_LL_GET_HW(LACT_MODULE), apb / MHZ / LACT_TICKS_PER_US); @@ -262,34 +301,20 @@ bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz) { #endif } #endif - //Update FreeRTOS Tick Divisor -#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 + // Update FreeRTOS Tick Divisor for Xtensa targets +#if TARGET_HAS_XTENSA_TICK uint32_t fcpu = (conf.freq_mhz >= 80) ? (conf.freq_mhz * MHZ) : (apb); _xt_tick_divisor = fcpu / XT_TICK_PER_SEC; #endif - //Call peripheral functions after the APB change + if (apb_change_callbacks) { triggerApbChangeCallback(APB_AFTER_CHANGE, capb, apb); } -#if defined(SOC_CLK_APLL_SUPPORTED) && !defined(CONFIG_IDF_TARGET_ESP32P4) // APLL not yet supported in ESP32-P4 - log_d( - "%s: %u / %u = %u Mhz, APB: %u Hz", - (conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_APLL) ? "APLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "8M")), - conf.source_freq_mhz, conf.div, conf.freq_mhz, apb - ); -#elif defined(CONFIG_IDF_TARGET_ESP32C5) - log_d( - "%s: %u / %u = %u Mhz, APB: %u Hz", - (conf.source == SOC_CPU_CLK_SRC_PLL_F240M || conf.source == SOC_CPU_CLK_SRC_PLL_F160M) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "8M"), - conf.source_freq_mhz, conf.div, conf.freq_mhz, apb - ); -#else - log_d( - "%s: %u / %u = %u Mhz, APB: %u Hz", (conf.source == SOC_CPU_CLK_SRC_PLL) ? "PLL" : ((conf.source == SOC_CPU_CLK_SRC_XTAL) ? "XTAL" : "17.5M"), - conf.source_freq_mhz, conf.div, conf.freq_mhz, apb - ); -#endif + + // ===== Debug logging ===== + log_d("%s: %u / %u = %u Mhz, APB: %u Hz", getClockSourceName(conf.source), conf.source_freq_mhz, conf.div, conf.freq_mhz, apb); + return true; } diff --git a/cores/esp32/esp32-hal-cpu.h b/cores/esp32/esp32-hal-cpu.h index 59806b460ae..dfae10678a1 100644 --- a/cores/esp32/esp32-hal-cpu.h +++ b/cores/esp32/esp32-hal-cpu.h @@ -23,6 +23,72 @@ extern "C" { #include #include +#include "sdkconfig.h" +#include "soc/soc_caps.h" + +// When adding a new target, update the appropriate group(s) below + +// Targets that support XTAL frequency queries via rtc_clk_xtal_freq_get() +#if (!defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32P4)) +#define TARGET_HAS_XTAL_FREQ 1 +#else +#define TARGET_HAS_XTAL_FREQ 0 +#endif + +// Targets that need dynamic APB frequency updates via rtc_clk_apb_freq_update() +#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C3)) +#define TARGET_HAS_DYNAMIC_APB 1 +#else +#define TARGET_HAS_DYNAMIC_APB 0 +#endif + +// Xtensa architecture targets that need FreeRTOS tick divisor updates +#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S2)) +#define TARGET_HAS_XTENSA_TICK 1 +#else +#define TARGET_HAS_XTENSA_TICK 0 +#endif + +// Targets with APLL support (uses IDF SOC capability macro) +// Note: ESP32-P4 APLL support is not yet fully implemented in IDF +#if (defined(SOC_CLK_APLL_SUPPORTED) && !defined(CONFIG_IDF_TARGET_ESP32P4)) +#define TARGET_HAS_APLL 1 +#else +#define TARGET_HAS_APLL 0 +#endif + +// Targets grouped by maximum CPU frequency support + +#if (defined(CONFIG_IDF_TARGET_ESP32P4)) +#define TARGET_CPU_FREQ_MAX_400 1 +#else +#define TARGET_CPU_FREQ_MAX_400 0 +#endif + +#if (defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C5) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3)) +#define TARGET_CPU_FREQ_MAX_240 1 +#else +#define TARGET_CPU_FREQ_MAX_240 0 +#endif + +#if (defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61)) +#define TARGET_CPU_FREQ_MAX_160 1 +#else +#define TARGET_CPU_FREQ_MAX_160 0 +#endif + +#if (defined(CONFIG_IDF_TARGET_ESP32C2)) +#define TARGET_CPU_FREQ_MAX_120 1 +#else +#define TARGET_CPU_FREQ_MAX_120 0 +#endif + +#if (defined(CONFIG_IDF_TARGET_ESP32H2)) +#define TARGET_CPU_FREQ_MAX_96 1 +#else +#define TARGET_CPU_FREQ_MAX_96 0 +#endif + typedef enum { APB_BEFORE_CHANGE, APB_AFTER_CHANGE @@ -40,6 +106,8 @@ bool removeApbChangeCallback(void *arg, apb_change_cb_t cb); // 24, 12 <<< For 24MHz XTAL bool setCpuFrequencyMhz(uint32_t cpu_freq_mhz); +const char *getSupportedCpuFrequencyMhz(uint8_t xtal); +const char *getClockSourceName(uint8_t source); uint32_t getCpuFrequencyMhz(); // In MHz uint32_t getXtalFrequencyMhz(); // In MHz uint32_t getApbFrequency(); // In Hz diff --git a/cores/esp32/esp32-hal-gpio.c b/cores/esp32/esp32-hal-gpio.c index 90ad1e7f36d..4a7fe6d1e75 100644 --- a/cores/esp32/esp32-hal-gpio.c +++ b/cores/esp32/esp32-hal-gpio.c @@ -33,20 +33,21 @@ #include "soc/touch_sensor_periph.h" int8_t digitalPinToTouchChannel(uint8_t pin) { - int8_t ret = -1; if (pin < SOC_GPIO_PIN_COUNT) { for (uint8_t i = 0; i < SOC_TOUCH_SENSOR_NUM; i++) { if (touch_sensor_channel_io_map[i] == pin) { - ret = i; - break; + return i; } } } - return ret; + + log_e("No touch pad on selected pin(%u)!", pin); + return -1; } #else // No Touch Sensor available int8_t digitalPinToTouchChannel(uint8_t pin) { + log_e("Touch sensor not available on this chip"); return -1; } #endif @@ -126,11 +127,15 @@ extern void ARDUINO_ISR_ATTR __pinMode(uint8_t pin, uint8_t mode) { gpiohal.dev = GPIO_LL_GET_HW(GPIO_PORT_0); gpio_config_t conf = { - .pin_bit_mask = (1ULL << pin), /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */ - .mode = GPIO_MODE_DISABLE, /*!< GPIO mode: set input/output mode */ - .pull_up_en = GPIO_PULLUP_DISABLE, /*!< GPIO pull-up */ - .pull_down_en = GPIO_PULLDOWN_DISABLE, /*!< GPIO pull-down */ + .pin_bit_mask = (1ULL << pin), /*!< GPIO pin: set with bit mask, each bit maps to a GPIO */ + .mode = GPIO_MODE_DISABLE, /*!< GPIO mode: set input/output mode */ + .pull_up_en = GPIO_PULLUP_DISABLE, /*!< GPIO pull-up */ + .pull_down_en = GPIO_PULLDOWN_DISABLE, /*!< GPIO pull-down */ +#ifndef CONFIG_IDF_TARGET_ESP32C61 .intr_type = gpiohal.dev->pin[pin].int_type /*!< GPIO interrupt type - previously set */ +#else + .intr_type = gpiohal.dev->pinn[pin].pinn_int_type /*!< GPIO interrupt type - previously set */ +#endif }; if (mode < 0x20) { //io conf.mode = mode & (INPUT | OUTPUT); diff --git a/cores/esp32/esp32-hal-hosted.c b/cores/esp32/esp32-hal-hosted.c new file mode 100644 index 00000000000..b5ab854dbc8 --- /dev/null +++ b/cores/esp32/esp32-hal-hosted.c @@ -0,0 +1,356 @@ +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) + +#include "esp32-hal-hosted.h" +#include "esp32-hal-log.h" +#include "esp32-hal.h" +#include "pins_arduino.h" + +#include "esp_hosted.h" +#include "esp_hosted_transport_config.h" +// extern esp_err_t esp_hosted_init(); +// extern esp_err_t esp_hosted_deinit(); + +static bool hosted_initialized = false; +static bool hosted_ble_active = false; +static bool hosted_wifi_active = false; + +static sdio_pin_config_t sdio_pin_config = { +#ifdef BOARD_HAS_SDIO_ESP_HOSTED + .pin_clk = BOARD_SDIO_ESP_HOSTED_CLK, + .pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD, + .pin_d0 = BOARD_SDIO_ESP_HOSTED_D0, + .pin_d1 = BOARD_SDIO_ESP_HOSTED_D1, + .pin_d2 = BOARD_SDIO_ESP_HOSTED_D2, + .pin_d3 = BOARD_SDIO_ESP_HOSTED_D3, + .pin_reset = BOARD_SDIO_ESP_HOSTED_RESET +#else + .pin_clk = CONFIG_ESP_SDIO_PIN_CLK, + .pin_cmd = CONFIG_ESP_SDIO_PIN_CMD, + .pin_d0 = CONFIG_ESP_SDIO_PIN_D0, + .pin_d1 = CONFIG_ESP_SDIO_PIN_D1, + .pin_d2 = CONFIG_ESP_SDIO_PIN_D2, + .pin_d3 = CONFIG_ESP_SDIO_PIN_D3, + .pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE +#endif +}; + +static esp_hosted_coprocessor_fwver_t slave_version_struct = {.major1 = 0, .minor1 = 0, .patch1 = 0}; +static esp_hosted_coprocessor_fwver_t host_version_struct = { + .major1 = ESP_HOSTED_VERSION_MAJOR_1, .minor1 = ESP_HOSTED_VERSION_MINOR_1, .patch1 = ESP_HOSTED_VERSION_PATCH_1 +}; + +static bool hostedInit(); +static bool hostedDeinit(); + +void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { + *major = host_version_struct.major1; + *minor = host_version_struct.minor1; + *patch = host_version_struct.patch1; +} + +void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch) { + *major = slave_version_struct.major1; + *minor = slave_version_struct.minor1; + *patch = slave_version_struct.patch1; +} + +bool hostedHasUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + uint32_t host_version = ESP_HOSTED_VERSION_VAL(host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1); + uint32_t slave_version = 0; + + esp_err_t ret = esp_hosted_get_coprocessor_fwversion(&slave_version_struct); + if (ret != ESP_OK) { + log_e("Could not get slave firmware version: %s", esp_err_to_name(ret)); + } else { + slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); + } + + log_i("Host firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1); + log_i("Slave firmware version: %" PRIu32 ".%" PRIu32 ".%" PRIu32, slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); + + // compare major.minor only + // slave_version &= 0xFFFFFF00; + // host_version &= 0xFFFFFF00; + + if (host_version == slave_version) { + log_i("Versions Match!"); + } else if (host_version > slave_version) { + log_w("Version on Host is NEWER than version on co-processor"); + log_w("Update URL: %s", hostedGetUpdateURL()); + return true; + } else { + log_w("Version on Host is OLDER than version on co-processor"); + } + return false; +} + +char *hostedGetUpdateURL() { + // https://espressif.github.io/arduino-esp32/hosted/esp32c6-v1.2.3.bin + static char url[92] = {0}; + snprintf( + url, 92, "https://espressif.github.io/arduino-esp32/hosted/%s-v%" PRIu32 ".%" PRIu32 ".%" PRIu32 ".bin", CONFIG_ESP_HOSTED_IDF_SLAVE_TARGET, + host_version_struct.major1, host_version_struct.minor1, host_version_struct.patch1 + ); + return url; +} + +bool hostedBeginUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + esp_err_t err = esp_hosted_slave_ota_begin(); + if (err != ESP_OK) { + log_e("Failed to begin Update: %s", esp_err_to_name(err)); + } + return err == ESP_OK; +} + +bool hostedWriteUpdate(uint8_t *buf, uint32_t len) { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + esp_err_t err = esp_hosted_slave_ota_write(buf, len); + if (err != ESP_OK) { + log_e("Failed to write Update: %s", esp_err_to_name(err)); + } + return err == ESP_OK; +} + +bool hostedEndUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + esp_err_t err = esp_hosted_slave_ota_end(); + if (err != ESP_OK) { + log_e("Failed to end Update: %s", esp_err_to_name(err)); + } + return err == ESP_OK; +} + +bool hostedActivateUpdate() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + // Activate can fail on older firmwares and that is not critical + uint32_t slave_version = ESP_HOSTED_VERSION_VAL(slave_version_struct.major1, slave_version_struct.minor1, slave_version_struct.patch1); + uint32_t min_version = ESP_HOSTED_VERSION_VAL(2, 6, 0); + + if (slave_version < min_version) { + // Silence messages caused by earlier versions + esp_log_level_set("rpc_core", ESP_LOG_NONE); + } + + esp_err_t err = esp_hosted_slave_ota_activate(); + + // Any further communication will result in logged errors + esp_log_level_set("sdmmc_io", ESP_LOG_NONE); + esp_log_level_set("H_SDIO_DRV", ESP_LOG_NONE); + + if (err != ESP_OK && slave_version >= min_version) { + log_e("Failed to activate Update: %s", esp_err_to_name(err)); + return false; + } + return true; +} + +static bool hostedInit() { + if (!hosted_initialized) { + log_i("Initializing ESP-Hosted"); + log_d( + "SDIO pins: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", sdio_pin_config.pin_clk, sdio_pin_config.pin_cmd, sdio_pin_config.pin_d0, + sdio_pin_config.pin_d1, sdio_pin_config.pin_d2, sdio_pin_config.pin_d3, sdio_pin_config.pin_reset + ); + hosted_initialized = true; + struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); + conf.pin_clk.pin = sdio_pin_config.pin_clk; + conf.pin_cmd.pin = sdio_pin_config.pin_cmd; + conf.pin_d0.pin = sdio_pin_config.pin_d0; + conf.pin_d1.pin = sdio_pin_config.pin_d1; + conf.pin_d2.pin = sdio_pin_config.pin_d2; + conf.pin_d3.pin = sdio_pin_config.pin_d3; + conf.pin_reset.pin = sdio_pin_config.pin_reset; + esp_err_t err = esp_hosted_sdio_set_config(&conf); + if (err != ESP_OK) { //&& err != ESP_ERR_NOT_ALLOWED) { // uncomment when second init is fixed + log_e("esp_hosted_sdio_set_config failed: %s", esp_err_to_name(err)); + return false; + } + err = esp_hosted_init(); + if (err != ESP_OK) { + log_e("esp_hosted_init failed: %s", esp_err_to_name(err)); + hosted_initialized = false; + return false; + } + log_i("ESP-Hosted initialized!"); + err = esp_hosted_connect_to_slave(); + if (err != ESP_OK) { + log_e("esp_hosted_connect_to_slave failed: %s", esp_err_to_name(err)); + hosted_initialized = false; + return false; + } + hostedHasUpdate(); + return true; + } + + // Attach pins to PeriMan here + // Slave chip model is CONFIG_IDF_SLAVE_TARGET + // sdio_pin_config.pin_clk + // sdio_pin_config.pin_cmd + // sdio_pin_config.pin_d0 + // sdio_pin_config.pin_d1 + // sdio_pin_config.pin_d2 + // sdio_pin_config.pin_d3 + // sdio_pin_config.pin_reset + + return true; +} + +static bool hostedDeinit() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + if (esp_hosted_deinit() != ESP_OK) { + log_e("esp_hosted_deinit failed!"); + return false; + } + + hosted_initialized = false; + return true; +} + +bool hostedInitBLE() { + log_i("Initializing ESP-Hosted for BLE"); + if (!hostedInit()) { + return false; + } + esp_err_t err = esp_hosted_bt_controller_init(); + if (err != ESP_OK) { + log_e("esp_hosted_bt_controller_init failed: %s", esp_err_to_name(err)); + return false; + } + err = esp_hosted_bt_controller_enable(); + if (err != ESP_OK) { + log_e("esp_hosted_bt_controller_enable failed: %s", esp_err_to_name(err)); + return false; + } + hosted_ble_active = true; + return true; +} + +bool hostedInitWiFi() { + log_i("Initializing ESP-Hosted for WiFi"); + hosted_wifi_active = true; + return hostedInit(); +} + +bool hostedDeinitBLE() { + log_i("Deinitializing ESP-Hosted for BLE"); + esp_err_t err = esp_hosted_bt_controller_disable(); + if (err != ESP_OK) { + log_e("esp_hosted_bt_controller_disable failed: %s", esp_err_to_name(err)); + return false; + } + err = esp_hosted_bt_controller_deinit(false); + if (err != ESP_OK) { + log_e("esp_hosted_bt_controller_deinit failed: %s", esp_err_to_name(err)); + return false; + } + hosted_ble_active = false; + if (!hosted_wifi_active) { + return hostedDeinit(); + } else { + log_i("ESP-Hosted is still being used by Wi-Fi. Skipping deinit."); + return true; + } +} + +bool hostedDeinitWiFi() { + log_i("Deinitializing ESP-Hosted for WiFi"); + hosted_wifi_active = false; + if (!hosted_ble_active) { + return hostedDeinit(); + } else { + log_i("ESP-Hosted is still being used by BLE. Skipping deinit."); + return true; + } +} + +bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { + if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) { + log_e("All SDIO pins must be defined"); + return false; + } + + if (hosted_initialized) { + int8_t current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst; + hostedGetPins(¤t_clk, ¤t_cmd, ¤t_d0, ¤t_d1, ¤t_d2, ¤t_d3, ¤t_rst); + log_e("SDIO pins must be set before ESP-Hosted is initialized"); + log_e( + "Current pins used: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, + current_rst + ); + return false; + } + + sdio_pin_config.pin_clk = clk; + sdio_pin_config.pin_cmd = cmd; + sdio_pin_config.pin_d0 = d0; + sdio_pin_config.pin_d1 = d1; + sdio_pin_config.pin_d2 = d2; + sdio_pin_config.pin_d3 = d3; + sdio_pin_config.pin_reset = rst; + return true; +} + +void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst) { + *clk = sdio_pin_config.pin_clk; + *cmd = sdio_pin_config.pin_cmd; + *d0 = sdio_pin_config.pin_d0; + *d1 = sdio_pin_config.pin_d1; + *d2 = sdio_pin_config.pin_d2; + *d3 = sdio_pin_config.pin_d3; + *rst = sdio_pin_config.pin_reset; +} + +bool hostedIsBLEActive() { + return hosted_ble_active; +} + +bool hostedIsWiFiActive() { + return hosted_wifi_active; +} + +bool hostedIsInitialized() { + return hosted_initialized; +} + +#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */ diff --git a/cores/esp32/esp32-hal-hosted.h b/cores/esp32/esp32-hal-hosted.h new file mode 100644 index 00000000000..1b329fcb469 --- /dev/null +++ b/cores/esp32/esp32-hal-hosted.h @@ -0,0 +1,61 @@ +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_ESP32_HAL_HOSTED_H_ +#define MAIN_ESP32_HAL_HOSTED_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) + +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t pin_clk; + uint8_t pin_cmd; + uint8_t pin_d0; + uint8_t pin_d1; + uint8_t pin_d2; + uint8_t pin_d3; + uint8_t pin_reset; +} sdio_pin_config_t; + +bool hostedInitBLE(); +bool hostedInitWiFi(); +bool hostedDeinitBLE(); +bool hostedDeinitWiFi(); +bool hostedIsInitialized(); +bool hostedIsBLEActive(); +bool hostedIsWiFiActive(); +bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst); +void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst); +void hostedGetHostVersion(uint32_t *major, uint32_t *minor, uint32_t *patch); +void hostedGetSlaveVersion(uint32_t *major, uint32_t *minor, uint32_t *patch); +bool hostedHasUpdate(); +char *hostedGetUpdateURL(); +bool hostedBeginUpdate(); +bool hostedWriteUpdate(uint8_t *buf, uint32_t len); +bool hostedEndUpdate(); +bool hostedActivateUpdate(); + +#ifdef __cplusplus +} +#endif + +#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */ +#endif /* MAIN_ESP32_HAL_HOSTED_H_ */ diff --git a/cores/esp32/esp32-hal-i2c-ng.c b/cores/esp32/esp32-hal-i2c-ng.c index a3b2307b8a8..8a55b369d9e 100644 --- a/cores/esp32/esp32-hal-i2c-ng.c +++ b/cores/esp32/esp32-hal-i2c-ng.c @@ -156,6 +156,9 @@ esp_err_t i2cInit(uint8_t i2c_num, int8_t sda, int8_t scl, uint32_t frequency) { } } + // Silence messages coming from the IDF driver + esp_log_level_set("i2c.master", ESP_LOG_NONE); + init_fail: #if !CONFIG_DISABLE_HAL_LOCKS //release lock diff --git a/cores/esp32/esp32-hal-i2c-slave.c b/cores/esp32/esp32-hal-i2c-slave.c index da3a819387a..5c0991c1981 100644 --- a/cores/esp32/esp32-hal-i2c-slave.c +++ b/cores/esp32/esp32-hal-i2c-slave.c @@ -44,7 +44,7 @@ #include "soc/periph_defs.h" #include "hal/i2c_ll.h" #include "hal/i2c_types.h" -#ifndef CONFIG_IDF_TARGET_ESP32C5 +#if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32C61) #include "hal/clk_gate_ll.h" #endif #include "esp32-hal-log.h" @@ -328,7 +328,8 @@ esp_err_t i2cSlaveInit(uint8_t num, int sda, int scl, uint16_t slaveID, uint32_t frequency = 100000L; } frequency = (frequency * 5) / 4; -#if !defined(CONFIG_IDF_TARGET_ESP32P4) && !defined(CONFIG_IDF_TARGET_ESP32C5) +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 \ + || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 if (i2c->num == 0) { periph_ll_enable_clk_clear_rst(PERIPH_I2C0_MODULE); #if SOC_HP_I2C_NUM > 1 @@ -618,7 +619,7 @@ static bool i2c_slave_check_line_state(int8_t sda, int8_t scl) { log_w("Recovered after %d Cycles", a); gpio_set_level(sda, 0); // start i2c_slave_delay_us(5); - for (uint8_t a = 0; a < 9; a++) { + for (uint8_t b = 0; b < 9; b++) { gpio_set_level(scl, 1); i2c_slave_delay_us(5); gpio_set_level(scl, 0); diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index ffb24db4599..1847f6af839 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -784,17 +784,23 @@ void analogWrite(uint8_t pin, int value) { } void analogWriteFrequency(uint8_t pin, uint32_t freq) { - if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) { - log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first"); - return; + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { // if pin is attached to LEDC change frequency, otherwise update the global frequency + if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) { + log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first"); + return; + } } analog_frequency = freq; } void analogWriteResolution(uint8_t pin, uint8_t resolution) { - if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) { - log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first"); - return; + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { // if pin is attached to LEDC change resolution, otherwise update the global resolution + if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) { + log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first"); + return; + } } analog_resolution = resolution; } diff --git a/cores/esp32/esp32-hal-matrix.c b/cores/esp32/esp32-hal-matrix.c index 0d81e979f2b..f609d9e1487 100644 --- a/cores/esp32/esp32-hal-matrix.c +++ b/cores/esp32/esp32-hal-matrix.c @@ -36,6 +36,8 @@ #include "esp32p4/rom/gpio.h" #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/gpio.h" +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/gpio.h" #else #error Target CONFIG_IDF_TARGET is not supported #endif diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 4c8a4e5beab..f0195ed1acf 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -30,8 +30,7 @@ #endif #include #include "soc/rtc.h" -#if !defined(CONFIG_IDF_TARGET_ESP32C2) && !defined(CONFIG_IDF_TARGET_ESP32C6) && !defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4) \ - && !defined(CONFIG_IDF_TARGET_ESP32C5) +#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) #include "soc/rtc_cntl_reg.h" #include "soc/syscon_reg.h" #endif @@ -59,7 +58,8 @@ #include "esp32p4/rom/rtc.h" #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/rtc.h" - +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/rtc.h" #else #error Target CONFIG_IDF_TARGET is not supported #endif @@ -308,7 +308,7 @@ void initArduino() { if (err) { log_e("Failed to initialize NVS! Error: %u", err); } -#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED +#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h") if (!btInUse()) { esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); } diff --git a/cores/esp32/esp32-hal-periman.c b/cores/esp32/esp32-hal-periman.c index 6ef3c3d984a..bec88b0fdeb 100644 --- a/cores/esp32/esp32-hal-periman.c +++ b/cores/esp32/esp32-hal-periman.c @@ -16,7 +16,7 @@ typedef struct ATTR_PACKED { int8_t bus_channel; } peripheral_pin_item_t; -static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX]; +static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX] = {NULL}; static peripheral_pin_item_t pins[SOC_GPIO_PIN_COUNT]; #define GPIO_NOT_VALID(p) ((p >= SOC_GPIO_PIN_COUNT) || ((SOC_GPIO_VALID_GPIO_MASK & (1ULL << p)) == 0)) @@ -236,6 +236,33 @@ bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t return true; } +// This no-op callback is used by perimanClearBusDeinit() to effectively disable bus deinit functionality +// without setting the callback to NULL, which would cause errors in perimanSetPinBus() at line 146. +static bool empty_bus_deinit_cb(void *bus) { + return true; +} + +bool perimanClearBusDeinit(peripheral_bus_type_t type) { + if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) { + log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type); + return false; + } + deinit_functions[type] = empty_bus_deinit_cb; + log_v("Deinit function for type %s (%u) cleared", perimanGetTypeName(type), (unsigned int)type); + return true; +} + +peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type) { + if (type >= ESP32_BUS_TYPE_MAX || type == ESP32_BUS_TYPE_INIT) { + log_e("Invalid type: %s (%u)", perimanGetTypeName(type), (unsigned int)type); + return NULL; + } + if (deinit_functions[type] == empty_bus_deinit_cb) { + return NULL; + } + return deinit_functions[type]; +} + bool perimanPinIsValid(uint8_t pin) { return !(GPIO_NOT_VALID(pin)); } diff --git a/cores/esp32/esp32-hal-periman.h b/cores/esp32/esp32-hal-periman.h index 217d62b8741..12563718e19 100644 --- a/cores/esp32/esp32-hal-periman.h +++ b/cores/esp32/esp32-hal-periman.h @@ -134,6 +134,13 @@ int8_t perimanGetPinBusChannel(uint8_t pin); // Sets the peripheral destructor callback. Used to destroy bus when pin is assigned another function bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t cb); +// Clears the peripheral destructor callback +bool perimanClearBusDeinit(peripheral_bus_type_t type); + +// Get the peripheral destructor callback. It allows changing/restoring the peripheral pin function detaching, if necessary +// returns NULL if none is set +peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type); + // Check if given pin is a valid GPIO number bool perimanPinIsValid(uint8_t pin); diff --git a/cores/esp32/esp32-hal-psram.c b/cores/esp32/esp32-hal-psram.c index 0d57a67ede4..9b985e81b2a 100644 --- a/cores/esp32/esp32-hal-psram.c +++ b/cores/esp32/esp32-hal-psram.c @@ -31,6 +31,8 @@ #include "esp32p4/rom/cache.h" #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/cache.h" +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/cache.h" #else #error Target CONFIG_IDF_TARGET is not supported #endif diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 7bca1a1b529..6d8a6c7dfae 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -285,7 +285,7 @@ bool rmtDeinit(int pin) { return false; } -static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, bool loop, uint32_t timeout_ms) { +static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, uint32_t loop, uint32_t timeout_ms) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { return false; @@ -303,11 +303,21 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl } } +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms); - log_v( - "GPIO: %d - Currently in Loop Mode: [%s] | Asked to Loop: %s, LoopCancel: %s", pin, bus->rmt_ch_is_looping ? "YES" : "NO", loop ? "YES" : "NO", - loopCancel ? "YES" : "NO" - ); + // loop parameter semantics: + // loop == 0: no looping (single transmission) + // loop == 1: infinite looping + // loop > 1: transmit the data 'loop' times + { + char buf[17]; // placeholder for up to maximum uint32_t value (4294967295) = 10 digits + " times" (6 chars) + null terminator (17 bytes) + snprintf(buf, sizeof(buf), "%lu times", loop); + log_v( + "GPIO: %d - Currently in Loop Mode: [%s] | Loop Request: [%s], LoopCancel: [%s]", pin, bus->rmt_ch_is_looping ? "YES" : "NO", + loop == 0 ? "NO" : (loop == 1 ? "FOREVER" : buf), loopCancel ? "YES" : "NO" + ); + } +#endif if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) { log_v("GPIO %d - RMT Write still pending to be completed.", pin); @@ -336,8 +346,8 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl bus->rmt_ch_is_looping = false; } else { // new writing | looping request // looping | Writing over a previous looping state is valid - if (loop) { - transmit_cfg.loop_count = -1; // enable infinite loop mode + if (loop > 0) { + transmit_cfg.loop_count = (loop == 1) ? -1 : loop; // keeps RMT_FLAG_TX_DONE set - it never changes } else { // looping mode never sets this flag (IDF 5.1) in the callback @@ -348,8 +358,10 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl retCode = false; log_w("GPIO %d - RMT Transmission failed.", pin); } else { // transmit OK - if (loop) { - bus->rmt_ch_is_looping = true; // for ever... until a channel canceling or new writing + if (loop > 0) { + // rmt_ch_is_looping is used as a flag to indicate that RMT is in looping execution in order to + // be canceled whenever a new _rmtWrite() is executed while it is looping + bus->rmt_ch_is_looping = true; } else { if (blocking) { // wait for transmission confirmation | timeout @@ -402,15 +414,36 @@ static bool _rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, bool wa } bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) { - return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, false /*looping*/, timeout_ms); + return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, 0 /*looping*/, timeout_ms); } bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) { - return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, false /*looping*/, 0 /*N/A*/); + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/); } bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols) { - return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, true /*looping*/, 0 /*N/A*/); + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 1 /*looping*/, 0 /*N/A*/); +} + +// Same as rmtWriteLooping(...) but it transmits the data a fixed number of times ("loop_count"). +// loop_count == 0 is invalid (no transmission); loop_count == 1 transmits once (no looping); loop_count > 1 transmits the data repeatedly (looping). +bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count) { + if (loop_count == 0) { + log_e("RMT TX GPIO %d : Invalid loop_count (%u). Must be at least 1.", pin, loop_count); + return false; + } + if (loop_count == 1) { + // send the RMT symbols once using non-blocking write (single non-looping transmission) + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/); + } else { + // write the RMT symbols for loop_count times +#if SOC_RMT_SUPPORT_TX_LOOP_COUNT + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, loop_count /*looping*/, 0 /*N/A*/); +#else + log_e("RMT TX GPIO %d : Loop Count is not supported. Writing failed.", pin); + return false; +#endif + } } bool rmtTransmitCompleted(int pin) { diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index c15eadfbcd1..2698f9dd898 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -124,7 +124,9 @@ bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeou bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); /** - Writing data up to the reserved memsize, looping continuously + Writing data up to the reserved memsize, looping continuously (rmtWriteLooping()) or fixed + number of times (rmtWriteRepeated()) + is a 32 bits structure as defined by rmt_data_t type. It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t @@ -133,9 +135,11 @@ bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); Non-Blocking mode - returns right after execution Returns on execution success, otherwise - will return always while it is looping. + will return always while it is looping mode. + looping mode is active for rmtWriteLooping() and for rmtWriteRepeated() when loop_count > 1. */ bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols); +bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count); /** Checks if transmission is completed and the rmtChannel ready for transmitting new data. diff --git a/cores/esp32/esp32-hal-spi.c b/cores/esp32/esp32-hal-spi.c index 0555dfae095..378ae587858 100644 --- a/cores/esp32/esp32-hal-spi.c +++ b/cores/esp32/esp32-hal-spi.c @@ -26,7 +26,7 @@ #include "soc/io_mux_reg.h" #include "soc/gpio_sig_map.h" #include "soc/rtc.h" -#ifndef CONFIG_IDF_TARGET_ESP32C5 +#if !defined(CONFIG_IDF_TARGET_ESP32C5) && !defined(CONFIG_IDF_TARGET_ESP32C61) #include "hal/clk_gate_ll.h" #endif #include "esp32-hal-periman.h" @@ -66,6 +66,9 @@ #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/ets_sys.h" #include "esp32c5/rom/gpio.h" +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/ets_sys.h" +#include "esp32c61/rom/gpio.h" #else #error Target CONFIG_IDF_TARGET is not supported #endif @@ -125,18 +128,7 @@ struct spi_struct_t { #define SPI_SS_IDX(p, n) ((p == 0) ? SPI_FSPI_SS_IDX(n) : ((p == 1) ? SPI_HSPI_SS_IDX(n) : 0)) -#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 -// ESP32C3 -#define SPI_COUNT (1) - -#define SPI_CLK_IDX(p) FSPICLK_OUT_IDX -#define SPI_MISO_IDX(p) FSPIQ_OUT_IDX -#define SPI_MOSI_IDX(p) FSPID_IN_IDX - -#define SPI_SPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : ((n == 2) ? FSPICS2_OUT_IDX : FSPICS0_OUT_IDX))) -#define SPI_SS_IDX(p, n) SPI_SPI_SS_IDX(n) - -#else +#elif CONFIG_IDF_TARGET_ESP32 // ESP32 #define SPI_COUNT (4) @@ -149,6 +141,17 @@ struct spi_struct_t { #define SPI_VSPI_SS_IDX(n) ((n == 0) ? VSPICS0_OUT_IDX : ((n == 1) ? VSPICS1_OUT_IDX : ((n == 2) ? VSPICS2_OUT_IDX : VSPICS0_OUT_IDX))) #define SPI_SS_IDX(p, n) ((p == 0) ? SPI_SPI_SS_IDX(n) : ((p == 1) ? SPI_SPI_SS_IDX(n) : ((p == 2) ? SPI_HSPI_SS_IDX(n) : ((p == 3) ? SPI_VSPI_SS_IDX(n) : 0)))) +#else +// ESP32C2, C3, C5, C6, C61, H2 +#define SPI_COUNT (1) + +#define SPI_CLK_IDX(p) FSPICLK_OUT_IDX +#define SPI_MISO_IDX(p) FSPIQ_OUT_IDX +#define SPI_MOSI_IDX(p) FSPID_IN_IDX + +#define SPI_SPI_SS_IDX(n) ((n == 0) ? FSPICS0_OUT_IDX : ((n == 1) ? FSPICS1_OUT_IDX : ((n == 2) ? FSPICS2_OUT_IDX : FSPICS0_OUT_IDX))) +#define SPI_SS_IDX(p, n) SPI_SPI_SS_IDX(n) + #endif #if CONFIG_DISABLE_HAL_LOCKS @@ -159,17 +162,13 @@ static spi_t _spi_bus_array[] = { #if CONFIG_IDF_TARGET_ESP32S2 ||CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 1, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C2 - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C3 - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 - {(spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} -#else +#elif CONFIG_IDF_TARGET_ESP32 {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), 1, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 2, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), 3, -1, -1, -1, -1, false} +#else // ESP32C2, C3, C5, C6, C61, H2 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), 0, -1, -1, -1, -1, false} #endif }; // clang-format on @@ -179,22 +178,21 @@ static spi_t _spi_bus_array[] = { } while (xSemaphoreTake(spi->lock, portMAX_DELAY) != pdPASS) #define SPI_MUTEX_UNLOCK() xSemaphoreGive(spi->lock) +// clang-format off static spi_t _spi_bus_array[] = { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4 - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 1, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C2 - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C3 - {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} -#elif CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C5 - {(spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} -#else + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false}, + {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 1, -1, -1, -1, -1, false} +#elif CONFIG_IDF_TARGET_ESP32 {(volatile spi_dev_t *)(DR_REG_SPI0_BASE), NULL, 0, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI1_BASE), NULL, 1, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 2, -1, -1, -1, -1, false}, {(volatile spi_dev_t *)(DR_REG_SPI3_BASE), NULL, 3, -1, -1, -1, -1, false} +#else // ESP32C2, C3, C5, C6, C61, H2 + {(volatile spi_dev_t *)(DR_REG_SPI2_BASE), NULL, 0, -1, -1, -1, -1, false} #endif }; +// clang-format on #endif static bool spiDetachBus(void *bus) { @@ -701,7 +699,7 @@ spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t spi->dev->clk_gate.clk_en = 1; spi->dev->clk_gate.mst_clk_sel = 1; spi->dev->clk_gate.mst_clk_active = 1; -#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) +#if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S3) spi->dev->dma_conf.tx_seg_trans_clr_en = 1; spi->dev->dma_conf.rx_seg_trans_clr_en = 1; spi->dev->dma_conf.dma_seg_trans_en = 0; @@ -712,10 +710,10 @@ spi_t *spiStartBus(uint8_t spi_num, uint32_t clockDiv, uint8_t dataMode, uint8_t spi->dev->user.doutdin = 1; int i; for (i = 0; i < 16; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = 0x00000000; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = 0x00000000; +#else + spi->dev->data_buf[i].val = 0x00000000; #endif } SPI_MUTEX_UNLOCK(); @@ -760,10 +758,10 @@ void spiWrite(spi_t *spi, const uint32_t *data, uint8_t len) { spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif for (i = 0; i < len; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i]; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i]; +#else + spi->dev->data_buf[i].val = data[i]; #endif } #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) @@ -787,10 +785,10 @@ void spiTransfer(spi_t *spi, uint32_t *data, uint8_t len) { spi->dev->mosi_dlen.usr_mosi_dbitlen = (len * 32) - 1; spi->dev->miso_dlen.usr_miso_dbitlen = (len * 32) - 1; for (i = 0; i < len; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i]; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i]; +#else + spi->dev->data_buf[i].val = data[i]; #endif } #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) @@ -800,10 +798,10 @@ void spiTransfer(spi_t *spi, uint32_t *data, uint8_t len) { spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); for (i = 0; i < len; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data[i] = spi->dev->data_buf[i].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data[i] = spi->dev->data_buf[i]; +#else + data[i] = spi->dev->data_buf[i].val; #endif } SPI_MUTEX_UNLOCK(); @@ -818,10 +816,10 @@ void spiWriteByte(spi_t *spi, uint8_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) @@ -840,10 +838,10 @@ uint8_t spiTransferByte(spi_t *spi, uint8_t data) { SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; spi->dev->miso_dlen.usr_miso_dbitlen = 7; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -851,10 +849,10 @@ uint8_t spiTransferByte(spi_t *spi, uint8_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val & 0xFF; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0] & 0xFF; +#else + data = spi->dev->data_buf[0].val & 0xFF; #endif SPI_MUTEX_UNLOCK(); return data; @@ -881,10 +879,10 @@ void spiWriteWord(spi_t *spi, uint16_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -905,10 +903,10 @@ uint16_t spiTransferWord(spi_t *spi, uint16_t data) { SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 15; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -916,10 +914,10 @@ uint16_t spiTransferWord(spi_t *spi, uint16_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0]; +#else + data = spi->dev->data_buf[0].val; #endif SPI_MUTEX_UNLOCK(); if (!spi->dev->ctrl.rd_bit_order) { @@ -940,10 +938,10 @@ void spiWriteLong(spi_t *spi, uint32_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -964,10 +962,10 @@ uint32_t spiTransferLong(spi_t *spi, uint32_t data) { SPI_MUTEX_LOCK(); spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 31; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -975,10 +973,10 @@ uint32_t spiTransferLong(spi_t *spi, uint32_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0]; +#else + data = spi->dev->data_buf[0].val; #endif SPI_MUTEX_UNLOCK(); if (!spi->dev->ctrl.rd_bit_order) { @@ -1014,10 +1012,10 @@ static void __spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, ui spi->dev->miso_dlen.usr_miso_dbitlen = ((bytes * 8) - 1); for (i = 0; i < words; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = wordsBuf[i]; //copy buffer to spi fifo -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = wordsBuf[i]; //copy buffer to spi fifo +#else + spi->dev->data_buf[i].val = wordsBuf[i]; //copy buffer to spi fifo #endif } @@ -1031,10 +1029,10 @@ static void __spiTransferBytes(spi_t *spi, const uint8_t *data, uint8_t *out, ui if (out) { for (i = 0; i < words; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - wordsBuf[i] = spi->dev->data_buf[i].val; //copy spi fifo to buffer -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 wordsBuf[i] = spi->dev->data_buf[i]; //copy spi fifo to buffer +#else + wordsBuf[i] = spi->dev->data_buf[i].val; //copy spi fifo to buffer #endif } memcpy(out, bytesBuf, bytes); //copy buffer to output @@ -1172,10 +1170,10 @@ void ARDUINO_ISR_ATTR spiWriteByteNL(spi_t *spi, uint8_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1191,10 +1189,10 @@ uint8_t spiTransferByteNL(spi_t *spi, uint8_t data) { } spi->dev->mosi_dlen.usr_mosi_dbitlen = 7; spi->dev->miso_dlen.usr_miso_dbitlen = 7; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1202,10 +1200,10 @@ uint8_t spiTransferByteNL(spi_t *spi, uint8_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val & 0xFF; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0] & 0xFF; +#else + data = spi->dev->data_buf[0].val & 0xFF; #endif return data; } @@ -1221,10 +1219,10 @@ void ARDUINO_ISR_ATTR spiWriteShortNL(spi_t *spi, uint16_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1243,10 +1241,10 @@ uint16_t spiTransferShortNL(spi_t *spi, uint16_t data) { } spi->dev->mosi_dlen.usr_mosi_dbitlen = 15; spi->dev->miso_dlen.usr_miso_dbitlen = 15; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1254,10 +1252,10 @@ uint16_t spiTransferShortNL(spi_t *spi, uint16_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val & 0xFFFF; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0] & 0xFFFF; +#else + data = spi->dev->data_buf[0].val & 0xFFFF; #endif if (!spi->dev->ctrl.rd_bit_order) { MSB_16_SET(data, data); @@ -1276,10 +1274,10 @@ void ARDUINO_ISR_ATTR spiWriteLongNL(spi_t *spi, uint32_t data) { #if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32 spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1298,10 +1296,10 @@ uint32_t spiTransferLongNL(spi_t *spi, uint32_t data) { } spi->dev->mosi_dlen.usr_mosi_dbitlen = 31; spi->dev->miso_dlen.usr_miso_dbitlen = 31; -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1309,10 +1307,10 @@ uint32_t spiTransferLongNL(spi_t *spi, uint32_t data) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0]; +#else + data = spi->dev->data_buf[0].val; #endif if (!spi->dev->ctrl.rd_bit_order) { MSB_32_SET(data, data); @@ -1340,10 +1338,10 @@ void spiWriteNL(spi_t *spi, const void *data_in, uint32_t len) { spi->dev->miso_dlen.usr_miso_dbitlen = 0; #endif for (size_t i = 0; i < c_longs; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i]; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i]; +#else + spi->dev->data_buf[i].val = data[i]; #endif } #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) @@ -1379,18 +1377,18 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint spi->dev->miso_dlen.usr_miso_dbitlen = (c_len * 8) - 1; if (data) { for (size_t i = 0; i < c_longs; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i]; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i]; +#else + spi->dev->data_buf[i].val = data[i]; #endif } } else { for (size_t i = 0; i < c_longs; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = 0xFFFFFFFF; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = 0xFFFFFFFF; +#else + spi->dev->data_buf[i].val = 0xFFFFFFFF; #endif } } @@ -1403,16 +1401,16 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint if (result) { if (c_len & 3) { for (size_t i = 0; i < (c_longs - 1); i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - result[i] = spi->dev->data_buf[i].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 result[i] = spi->dev->data_buf[i]; +#else + result[i] = spi->dev->data_buf[i].val; #endif } -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - uint32_t last_data = spi->dev->data_buf[c_longs - 1].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 uint32_t last_data = spi->dev->data_buf[c_longs - 1]; +#else + uint32_t last_data = spi->dev->data_buf[c_longs - 1].val; #endif uint8_t *last_out8 = (uint8_t *)&result[c_longs - 1]; uint8_t *last_data8 = (uint8_t *)&last_data; @@ -1421,10 +1419,10 @@ void spiTransferBytesNL(spi_t *spi, const void *data_in, uint8_t *data_out, uint } } else { for (size_t i = 0; i < c_longs; i++) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - result[i] = spi->dev->data_buf[i].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 result[i] = spi->dev->data_buf[i]; +#else + result[i] = spi->dev->data_buf[i].val; #endif } } @@ -1463,10 +1461,10 @@ void spiTransferBitsNL(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits) { spi->dev->mosi_dlen.usr_mosi_dbitlen = (bits - 1); spi->dev->miso_dlen.usr_miso_dbitlen = (bits - 1); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[0].val = data; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[0] = data; +#else + spi->dev->data_buf[0].val = data; #endif #if !defined(CONFIG_IDF_TARGET_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32S2) spi->dev->cmd.update = 1; @@ -1474,10 +1472,10 @@ void spiTransferBitsNL(spi_t *spi, uint32_t data, uint32_t *out, uint8_t bits) { #endif spi->dev->cmd.usr = 1; while (spi->dev->cmd.usr); -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - data = spi->dev->data_buf[0].val; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 data = spi->dev->data_buf[0]; +#else + data = spi->dev->data_buf[0].val; #endif if (out) { *out = data; @@ -1515,30 +1513,30 @@ void ARDUINO_ISR_ATTR spiWritePixelsNL(spi_t *spi, const void *data_in, uint32_t if (msb) { if (l_bytes && i == (c_longs - 1)) { if (l_bytes == 2) { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - MSB_16_SET(spi->dev->data_buf[i].val, data[i]); -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 MSB_16_SET(spi->dev->data_buf[i], data[i]); +#else + MSB_16_SET(spi->dev->data_buf[i].val, data[i]); #endif } else { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i] & 0xFF; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i] & 0xFF; +#else + spi->dev->data_buf[i].val = data[i] & 0xFF; #endif } } else { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - MSB_PIX_SET(spi->dev->data_buf[i].val, data[i]); -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 MSB_PIX_SET(spi->dev->data_buf[i], data[i]); +#else + MSB_PIX_SET(spi->dev->data_buf[i].val, data[i]); #endif } } else { -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - spi->dev->data_buf[i].val = data[i]; -#else +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 spi->dev->data_buf[i] = data[i]; +#else + spi->dev->data_buf[i].val = data[i]; #endif } } diff --git a/cores/esp32/esp32-hal-spi.h b/cores/esp32/esp32-hal-spi.h index 0284fea8829..b83d199c54d 100644 --- a/cores/esp32/esp32-hal-spi.h +++ b/cores/esp32/esp32-hal-spi.h @@ -32,7 +32,7 @@ extern "C" { #define HSPI 2 //SPI 2 bus normally mapped to pins 12 - 15, but can be matrixed to any pins #define VSPI 3 //SPI 3 bus normally attached to pins 5, 18, 19 and 23, but can be matrixed to any pins #else -#define FSPI 0 // ESP32C2, C3, C6, H2, S2, S3, P4 - SPI 2 bus +#define FSPI 0 // ESP32C2, C3, C5, C6, C61, H2, S2, S3, P4 - SPI 2 bus #define HSPI 1 // ESP32S2, S3, P4 - SPI 3 bus #endif diff --git a/cores/esp32/esp32-hal-time.c b/cores/esp32/esp32-hal-time.c index 074e999be71..e66a68fa271 100644 --- a/cores/esp32/esp32-hal-time.c +++ b/cores/esp32/esp32-hal-time.c @@ -22,24 +22,24 @@ #endif static void setTimeZone(long offset, int daylight) { - char cst[17] = {0}; - char cdt[17] = "DST"; - char tz[33] = {0}; + char cst[21] = {0}; + char cdt[21] = "DST"; + char tz[41] = {0}; if (offset % 3600) { - sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); + snprintf(cst, sizeof(cst), "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); } else { - sprintf(cst, "UTC%ld", offset / 3600); + snprintf(cst, sizeof(cst), "UTC%ld", offset / 3600); } if (daylight != 3600) { long tz_dst = offset - daylight; if (tz_dst % 3600) { - sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); + snprintf(cdt, sizeof(cdt), "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); } else { - sprintf(cdt, "DST%ld", tz_dst / 3600); + snprintf(cdt, sizeof(cdt), "DST%ld", tz_dst / 3600); } } - sprintf(tz, "%s%s", cst, cdt); + snprintf(tz, sizeof(tz), "%s%s", cst, cdt); setenv("TZ", tz, 1); tzset(); } diff --git a/cores/esp32/esp32-hal-tinyusb.c b/cores/esp32/esp32-hal-tinyusb.c index 30a827baa01..ed1ff2aab60 100644 --- a/cores/esp32/esp32-hal-tinyusb.c +++ b/cores/esp32/esp32-hal-tinyusb.c @@ -477,6 +477,25 @@ __attribute__((weak)) uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint __attribute__((weak)) void tud_network_init_cb(void) {} #endif +#if CFG_TUH_HID +__attribute__((weak)) void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report_desc, uint16_t desc_len) {} +__attribute__((weak)) void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t idx) {} +__attribute__((weak)) void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {} +__attribute__((weak)) void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const *report, uint16_t len) {} +__attribute__((weak)) void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {} +__attribute__((weak)) void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len) {} +__attribute__((weak)) void tuh_hid_set_protocol_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t protocol) {} +#endif +#if CFG_TUH_CDC +__attribute__((weak)) void tuh_cdc_mount_cb(uint8_t idx) {} +__attribute__((weak)) void tuh_cdc_umount_cb(uint8_t idx) {} +__attribute__((weak)) void tuh_cdc_rx_cb(uint8_t idx) {} +__attribute__((weak)) void tuh_cdc_tx_complete_cb(uint8_t idx) {} +#endif +#if CFG_TUH_MSC +__attribute__((weak)) void tuh_msc_mount_cb(uint8_t dev_addr) {} +__attribute__((weak)) void tuh_msc_umount_cb(uint8_t dev_addr) {} +#endif /* * Private API * */ diff --git a/cores/esp32/esp32-hal-touch-ng.c b/cores/esp32/esp32-hal-touch-ng.c index 888a299ec0c..a28ee4994c4 100644 --- a/cores/esp32/esp32-hal-touch-ng.c +++ b/cores/esp32/esp32-hal-touch-ng.c @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "soc/soc_caps.h" +#include "esp_idf_version.h" #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 for now +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 -#include "driver/touch_sens.h" #include "esp32-hal-touch-ng.h" #include "esp32-hal-periman.h" @@ -37,11 +37,24 @@ typedef struct { static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = { 0, }; - -static uint8_t _sample_num = 1; +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +static uint8_t _sample_num = 1; // only one sample configuration supported +static float _duration_ms = 5.0f; +static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5; +static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_1V7; +static touch_intr_trig_mode_t _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH; +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +static uint8_t _sample_num = 1; // only one sample configuration supported +static uint32_t _chg_times = 500; +static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5; +static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_2V2; +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 +static uint8_t _sample_num = 1; // TODO: can be extended to multiple samples static uint32_t _div_num = 1; static uint8_t _coarse_freq_tune = 1; static uint8_t _fine_freq_tune = 1; +#endif + static uint8_t used_pads = 0; static uint32_t __touchSleepTime = 256; @@ -104,8 +117,12 @@ bool touchDisable() { if (!enabled) { // Already disabled return true; } - if (!running && (touch_sensor_disable(touch_sensor_handle) != ESP_OK)) { - log_e("Touch sensor still running or disable failed!"); + if (running) { + log_e("Touch sensor still running!"); + return false; + } + if (touch_sensor_disable(touch_sensor_handle) != ESP_OK) { + log_e("Touch sensor disable failed!"); return false; } enabled = false; @@ -116,8 +133,12 @@ bool touchStart() { if (running) { // Already running return true; } - if (enabled && (touch_sensor_start_continuous_scanning(touch_sensor_handle) != ESP_OK)) { - log_e("Touch sensor not enabled or failed to start continuous scanning failed!"); + if (!enabled) { + log_e("Touch sensor not enabled!"); + return false; + } + if (touch_sensor_start_continuous_scanning(touch_sensor_handle) != ESP_OK) { + log_e("Touch sensor failed to start continuous scanning!"); return false; } running = true; @@ -156,15 +177,28 @@ bool touchBenchmarkThreshold(uint8_t pad) { // Reconfigure passed pad with new threshold uint32_t benchmark[_sample_num] = {}; +#if SOC_TOUCH_SUPPORT_BENCHMARK // ESP32S2, ESP32S3,ESP32P4 if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark) != ESP_OK) { log_e("Touch channel read data failed!"); return false; } +#else + if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, benchmark) != ESP_OK) { + log_e("Touch channel read data failed!"); + return false; + } +#endif + /* Calculate the proper active thresholds regarding the initial benchmark */ - touch_channel_config_t chan_cfg = {}; + touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG(); for (int i = 0; i < _sample_num; i++) { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + chan_cfg.abs_active_thresh[i] = (uint32_t)(benchmark[i] * (1 - s_thresh2bm_ratio)); + log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.abs_active_thresh[i]); +#else chan_cfg.active_thresh[i] = (uint32_t)(benchmark[i] * s_thresh2bm_ratio); log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.active_thresh[i]); +#endif } /* Update the channel configuration */ if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) { @@ -178,17 +212,26 @@ static bool touchDetachBus(void *pin) { int8_t pad = digitalPinToTouchChannel((int)(pin - 1)); channels_initialized[pad] = false; //disable touch pad and delete the channel + if (!touchStop()) { + log_e("touchStop() failed!"); + return false; + } + if (!touchDisable()) { + log_e("touchDisable() failed!"); + return false; + } touch_sensor_del_channel(touch_channel_handle[pad]); used_pads--; if (used_pads == 0) { - touchStop(); - touchDisable(); if (touch_sensor_del_controller(touch_sensor_handle) != ESP_OK) //deinit touch module, as no pads are used { log_e("Touch module deinit failed!"); return false; } initialized = false; + } else { + touchEnable(); + touchStart(); } return true; } @@ -198,21 +241,40 @@ static void __touchInit() { return; } // Support only one sample configuration for now +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V1_DEFAULT_SAMPLE_CONFIG(_duration_ms, _volt_low, _volt_high); +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 + touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V2_DEFAULT_SAMPLE_CONFIG(_chg_times, _volt_low, _volt_high); +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(_div_num, _coarse_freq_tune, _fine_freq_tune); +#endif touch_sensor_sample_config_t sample_cfg[_sample_num] = {}; sample_cfg[0] = single_sample_cfg; - /* Allocate new touch controller handle */ touch_sensor_config_t sens_cfg = { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + .power_on_wait_us = __touchSleepTime, + .meas_interval_us = __touchMeasureTime, + .intr_trig_mode = _intr_trig_mode, + .intr_trig_group = TOUCH_INTR_TRIG_GROUP_BOTH, + .sample_cfg_num = _sample_num, + .sample_cfg = sample_cfg, +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 + .power_on_wait_us = __touchSleepTime, + .meas_interval_us = __touchMeasureTime, + .max_meas_time_us = 0, + .sample_cfg_num = _sample_num, + .sample_cfg = sample_cfg, +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 .power_on_wait_us = __touchSleepTime, .meas_interval_us = __touchMeasureTime, .max_meas_time_us = 0, .output_mode = TOUCH_PAD_OUT_AS_CLOCK, .sample_cfg_num = _sample_num, .sample_cfg = sample_cfg, +#endif }; - // touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(_sample_num, sample_cfg); if (touch_sensor_new_controller(&sens_cfg, &touch_sensor_handle) != ESP_OK) { goto err; } @@ -225,14 +287,10 @@ static void __touchInit() { } /* Register the touch sensor on_active and on_inactive callbacks */ - touch_event_callbacks_t callbacks = { - .on_active = __touchOnActiveISR, - .on_inactive = __touchOnInactiveISR, - .on_measure_done = NULL, - .on_scan_done = NULL, - .on_timeout = NULL, - .on_proximity_meas_done = NULL, - }; + touch_event_callbacks_t callbacks = {0}; + callbacks.on_active = __touchOnActiveISR; + callbacks.on_inactive = __touchOnInactiveISR; + if (touch_sensor_register_callbacks(touch_sensor_handle, &callbacks, NULL) != ESP_OK) { goto err; } @@ -253,9 +311,7 @@ static void __touchChannelInit(int pad) { // Initial setup with default Threshold __touchInterruptHandlers[pad].fn = NULL; - touch_channel_config_t chan_cfg = { - .active_thresh = {1000} // default threshold, will be updated after benchmark - }; + touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG(); if (!touchStop() || !touchDisable()) { log_e("Touch sensor stop and disable failed!"); @@ -284,7 +340,6 @@ static void __touchChannelInit(int pad) { static touch_value_t __touchRead(uint8_t pin) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return 0; } @@ -312,7 +367,6 @@ static touch_value_t __touchRead(uint8_t pin) { static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, bool callWithArgs, touch_value_t threshold) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return; } @@ -323,8 +377,21 @@ static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Ar __touchInterruptHandlers[pad].arg = NULL; } else { // attach ISR User Call - __touchInit(); - __touchChannelInit(pad); + if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus); + if (!perimanClearPinBus(pin)) { + log_e("Failed to clear pin bus"); + return; + } + __touchInit(); + __touchChannelInit(pad); + + if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) { + touchDetachBus((void *)(pin + 1)); + log_e("Failed to set bus to Peripheral manager"); + return; + } + } __touchInterruptHandlers[pad].fn = userFunc; __touchInterruptHandlers[pad].callWithArgs = callWithArgs; __touchInterruptHandlers[pad].arg = Args; @@ -336,9 +403,13 @@ static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Ar return; } - touch_channel_config_t chan_cfg = {}; + touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG(); for (int i = 0; i < _sample_num; i++) { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + chan_cfg.abs_active_thresh[i] = threshold; +#else chan_cfg.active_thresh[i] = threshold; +#endif } if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) { @@ -353,17 +424,17 @@ static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Ar // it keeps backwards compatibility static void __touchAttachInterrupt(uint8_t pin, void (*userFunc)(void), touch_value_t threshold) { - __touchConfigInterrupt(pin, userFunc, NULL, threshold, false); + __touchConfigInterrupt(pin, userFunc, NULL, false, threshold); } // new additional version of the API with User Args static void __touchAttachArgsInterrupt(uint8_t pin, void (*userFunc)(void), void *args, touch_value_t threshold) { - __touchConfigInterrupt(pin, userFunc, args, threshold, true); + __touchConfigInterrupt(pin, userFunc, args, true, threshold); } // new additional API to detach touch ISR static void __touchDettachInterrupt(uint8_t pin) { - __touchConfigInterrupt(pin, NULL, NULL, 0, false); // userFunc as NULL acts as detaching + __touchConfigInterrupt(pin, NULL, NULL, false, 0); // userFunc as NULL acts as detaching } // /* @@ -375,14 +446,12 @@ bool touchInterruptGetLastStatus(uint8_t pin) { if (pad < 0) { return false; } - return __touchInterruptHandlers[pad].lastStatusIsPressed; } void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return; } @@ -405,9 +474,13 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) { touch_sleep_config_t deep_slp_cfg = { .slp_wakeup_lvl = TOUCH_DEEP_SLEEP_WAKEUP, +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + .deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration +#else // SOC_TOUCH_SENSOR_VERSION 2 and 3// ESP32S2, ESP32S3, ESP32P4 .deep_slp_chan = touch_channel_handle[pad], .deep_slp_thresh = {threshold}, .deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration +#endif }; // Register the deep sleep wake-up @@ -434,6 +507,29 @@ void touchSetTiming(float measure, uint32_t sleep) { __touchMeasureTime = measure; } +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) { + if (initialized) { + log_e("Touch sensor already initialized. Cannot set configuration."); + return; + } + _duration_ms = duration_ms; + _volt_low = volt_low; + _volt_high = volt_high; +} + +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) { + if (initialized) { + log_e("Touch sensor already initialized. Cannot set configuration."); + return; + } + _chg_times = chg_times; + _volt_low = volt_low; + _volt_high = volt_high; +} + +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune) { if (initialized) { log_e("Touch sensor already initialized. Cannot set configuration."); @@ -443,11 +539,22 @@ void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_fre _coarse_freq_tune = coarse_freq_tune; _fine_freq_tune = fine_freq_tune; } +#endif + +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +void touchInterruptSetThresholdDirection(bool mustbeLower) { + if (mustbeLower) { + _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH; + } else { + _intr_trig_mode = TOUCH_INTR_TRIG_ON_ABOVE_THRESH; + } +} +#endif extern touch_value_t touchRead(uint8_t) __attribute__((weak, alias("__touchRead"))); extern void touchAttachInterrupt(uint8_t, voidFuncPtr, touch_value_t) __attribute__((weak, alias("__touchAttachInterrupt"))); extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value_t) __attribute__((weak, alias("__touchAttachArgsInterrupt"))); extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt"))); -#endif /* SOC_TOUCH_SENSOR_VERSION == 3 */ +#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ diff --git a/cores/esp32/esp32-hal-touch-ng.h b/cores/esp32/esp32-hal-touch-ng.h index 0d4eb79ac58..ebe49466bb9 100644 --- a/cores/esp32/esp32-hal-touch-ng.h +++ b/cores/esp32/esp32-hal-touch-ng.h @@ -21,14 +21,31 @@ #define MAIN_ESP32_HAL_TOUCH_NEW_H_ #include "soc/soc_caps.h" +#include "esp_idf_version.h" + #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 #ifdef __cplusplus extern "C" { #endif #include "esp32-hal.h" +#include "driver/touch_sens.h" + +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { \ + .abs_active_thresh = {1000}, .charge_speed = TOUCH_CHARGE_SPEED_7, .init_charge_volt = TOUCH_INIT_CHARGE_VOLT_DEFAULT, \ + .group = TOUCH_CHAN_TRIG_GROUP_BOTH, \ + } +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32-S2 & ESP32-S3 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { .active_thresh = {2000}, .charge_speed = TOUCH_CHARGE_SPEED_7, .init_charge_volt = TOUCH_INIT_CHARGE_VOLT_DEFAULT, } +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32-P4 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { .active_thresh = {1000}, } +#endif typedef uint32_t touch_value_t; @@ -40,11 +57,39 @@ typedef uint32_t touch_value_t; **/ void touchSetTiming(float measure, uint32_t sleep); +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +/* + * @param[in] duration_ms The measurement duration of the touch channel + * @param[in] volt_low The low voltage limit of the touch channel + * @param[in] volt_high The high voltage limit of the touch channel + */ +void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high); + +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +/* + * @param[in] chg_times The charge times of the touch channel + * @param[in] volt_low The low voltage limit of the touch channel + * @param[in] volt_high The high voltage limit of the touch channel + */ +void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high); + +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 /* * Tune the touch pad frequency. * Note: Must be called before setting up touch pads */ void touchSetConfig(uint32_t _div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune); +#endif + +#if SOC_TOUCH_SENSOR_VERSION == 1 +/* + * Specific functions to ESP32 + * Tells the driver if it shall activate the ISR if the sensor is Lower or Higher than the Threshold + * Default if Lower. + * Note: Must be called before setting up touch pads + **/ +void touchInterruptSetThresholdDirection(bool mustbeLower); +#endif /* * Read touch pad value. @@ -86,6 +131,6 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold); } #endif -#endif /* SOC_TOUCH_SENSOR_VERSION == 3 */ +#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ #endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/cores/esp32/esp32-hal-touch.c b/cores/esp32/esp32-hal-touch.c index 701bf6d16c9..e27064d20e1 100644 --- a/cores/esp32/esp32-hal-touch.c +++ b/cores/esp32/esp32-hal-touch.c @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "soc/soc_caps.h" +#include "esp_idf_version.h" #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 #include "driver/touch_sensor.h" #include "esp32-hal-touch.h" @@ -205,7 +206,6 @@ static void __touchChannelInit(int pad) { static touch_value_t __touchRead(uint8_t pin) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return 0; } @@ -232,7 +232,6 @@ static touch_value_t __touchRead(uint8_t pin) { static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Args, touch_value_t threshold, bool callWithArgs) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return; } @@ -294,7 +293,6 @@ bool touchInterruptGetLastStatus(uint8_t pin) { void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) { int8_t pad = digitalPinToTouchChannel(pin); if (pad < 0) { - log_e(" No touch pad on selected pin!"); return; } @@ -325,5 +323,5 @@ extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt"))); extern void touchSetCycles(uint16_t, uint16_t) __attribute__((weak, alias("__touchSetCycles"))); -#endif /* SOC_TOUCH_SENSOR_VERSION <= 2 */ +#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ diff --git a/cores/esp32/esp32-hal-touch.h b/cores/esp32/esp32-hal-touch.h index 4b06c7db766..44c99dce206 100644 --- a/cores/esp32/esp32-hal-touch.h +++ b/cores/esp32/esp32-hal-touch.h @@ -21,8 +21,10 @@ #define MAIN_ESP32_HAL_TOUCH_H_ #include "soc/soc_caps.h" +#include "esp_idf_version.h" + #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32 ESP32S2 ESP32S3 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 #ifdef __cplusplus extern "C" { @@ -98,6 +100,6 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold); } #endif -#endif /* SOC_TOUCH_SENSOR_VERSION <= 2 */ +#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ #endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 8bd446a8eb8..4b80108fb0e 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -42,6 +42,9 @@ static int s_uart_debug_nr = 0; // UART number for debug output #define REF_TICK_BAUDRATE_LIMIT 250000 // this is maximum UART badrate using REF_TICK as clock +/* C prototype for the notifier implemented in HardwareSerial.cpp */ +extern void hal_uart_notify_pins_detached(int uart_num); + struct uart_struct_t { #if !CONFIG_DISABLE_HAL_LOCKS @@ -59,7 +62,8 @@ struct uart_struct_t { uint16_t _rx_buffer_size, _tx_buffer_size; // UART RX and TX buffer sizes bool _inverted; // UART inverted signal uint8_t _rxfifo_full_thrhd; // UART RX FIFO full threshold - int8_t _uart_clock_source; // UART Clock Source used when it is started using uartBegin() + int8_t _uart_clock_source; // UART Clock Source that should be used if user defines an specific one with setClockSource() + uint32_t inv_mask; // UART inverse mask used to maintain related pin state }; #if CONFIG_DISABLE_HAL_LOCKS @@ -68,21 +72,21 @@ struct uart_struct_t { #define UART_MUTEX_UNLOCK() static uart_t _uart_bus_array[] = { - {0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #if SOC_UART_NUM > 1 - {1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 2 - {2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 3 - {3, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {3, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 4 - {4, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {4, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 5 - {5, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {5, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif }; @@ -97,21 +101,21 @@ static uart_t _uart_bus_array[] = { xSemaphoreGive(uart->lock) static uart_t _uart_bus_array[] = { - {NULL, 0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 0, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #if SOC_UART_NUM > 1 - {NULL, 1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 1, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 2 - {NULL, 2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 2, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 3 - {NULL, 3, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 3, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 4 - {NULL, 4, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 4, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif #if SOC_UART_NUM > 5 - {NULL, 5, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1}, + {NULL, 5, false, 0, NULL, -1, -1, -1, -1, 0, 0, 0, 0, false, 0, -1, 0}, #endif }; @@ -281,29 +285,67 @@ static bool _uartDetachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t // Peripheral Manager detach callback for each specific UART PIN static bool _uartDetachBus_RX(void *busptr) { // sanity check - it should never happen - assert(busptr && "_uartDetachBus_RX bus NULL pointer."); + if (busptr == NULL) { + log_e("_uartDetachBus_RX: busptr is NULL"); + return false; + } uart_t *bus = (uart_t *)busptr; + if (bus->_rxPin < 0) { + log_d("_uartDetachBus_RX: RX pin already detached for UART%d", bus->num); + return true; + } + if (bus->_txPin < 0) { // both rx and tx pins are detached, terminate the uart driver + log_d("_uartDetachBus_RX: both RX and TX pins detached for UART%d, terminating driver", bus->num); + hal_uart_notify_pins_detached(bus->num); + return true; + } return _uartDetachPins(bus->num, bus->_rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); } static bool _uartDetachBus_TX(void *busptr) { // sanity check - it should never happen - assert(busptr && "_uartDetachBus_TX bus NULL pointer."); + if (busptr == NULL) { + log_e("_uartDetachBus_TX: busptr is NULL"); + return false; + } uart_t *bus = (uart_t *)busptr; + if (bus->_txPin < 0) { + log_d("_uartDetachBus_TX: TX pin already detached for UART%d", bus->num); + return true; + } + if (bus->_rxPin < 0) { // both rx and tx pins are detached, terminate the uart driver + log_d("_uartDetachBus_TX: both RX and TX pins detached for UART%d, terminating driver", bus->num); + hal_uart_notify_pins_detached(bus->num); + return true; + } return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, bus->_txPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); } static bool _uartDetachBus_CTS(void *busptr) { // sanity check - it should never happen - assert(busptr && "_uartDetachBus_CTS bus NULL pointer."); + if (busptr == NULL) { + log_e("_uartDetachBus_CTS: busptr is NULL"); + return false; + } uart_t *bus = (uart_t *)busptr; + if (bus->_ctsPin < 0) { + log_d("_uartDetachBus_CTS: CTS pin already detached for UART%d", bus->num); + return true; + } return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_ctsPin, UART_PIN_NO_CHANGE); } static bool _uartDetachBus_RTS(void *busptr) { // sanity check - it should never happen - assert(busptr && "_uartDetachBus_RTS bus NULL pointer."); + if (busptr == NULL) { + log_e("_uartDetachBus_RTS: busptr is NULL"); + return false; + } uart_t *bus = (uart_t *)busptr; + if (bus->_rtsPin < 0) { + log_d("_uartDetachBus_RTS: RTS pin already detached for UART%d", bus->num); + return true; + } return _uartDetachPins(bus->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, bus->_rtsPin); } @@ -479,6 +521,10 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ret &= perimanSetPinBus(rxPin, ESP32_BUS_TYPE_UART_RX, (void *)uart, uart_num, -1); if (ret) { uart->_rxPin = rxPin; + // set Peripheral Manager deInit Callback for this UART pin + if (perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RX) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RX, _uartDetachBus_RX); + } } } if (!ret) { @@ -502,6 +548,10 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ret &= perimanSetPinBus(txPin, ESP32_BUS_TYPE_UART_TX, (void *)uart, uart_num, -1); if (ret) { uart->_txPin = txPin; + // set Peripheral Manager deInit Callback for this UART pin + if (perimanGetBusDeinit(ESP32_BUS_TYPE_UART_TX) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_TX, _uartDetachBus_TX); + } } } if (!ret) { @@ -525,6 +575,10 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ret &= perimanSetPinBus(ctsPin, ESP32_BUS_TYPE_UART_CTS, (void *)uart, uart_num, -1); if (ret) { uart->_ctsPin = ctsPin; + // set Peripheral Manager deInit Callback for this UART pin + if (perimanGetBusDeinit(ESP32_BUS_TYPE_UART_CTS) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_CTS, _uartDetachBus_CTS); + } } } if (!ret) { @@ -548,6 +602,10 @@ static bool _uartAttachPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ret &= perimanSetPinBus(rtsPin, ESP32_BUS_TYPE_UART_RTS, (void *)uart, uart_num, -1); if (ret) { uart->_rtsPin = rtsPin; + // set Peripheral Manager deInit Callback for this UART pin + if (perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RTS) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RTS, _uartDetachBus_RTS); + } } } if (!ret) { @@ -567,14 +625,6 @@ int8_t uart_get_TxPin(uint8_t uart_num) { return _uart_bus_array[uart_num]._txPin; } -void uart_init_PeriMan(void) { - // set Peripheral Manager deInit Callback for each UART pin - perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RX, _uartDetachBus_RX); - perimanSetBusDeinit(ESP32_BUS_TYPE_UART_TX, _uartDetachBus_TX); - perimanSetBusDeinit(ESP32_BUS_TYPE_UART_CTS, _uartDetachBus_CTS); - perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RTS, _uartDetachBus_RTS); -} - // Routines that take care of UART events will be in the HardwareSerial Class code void uartGetEventQueue(uart_t *uart, QueueHandle_t *q) { // passing back NULL for the Queue pointer when UART is not initialized yet @@ -620,6 +670,16 @@ bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, in //log_v("setting UART%d pins: prev->new RX(%d->%d) TX(%d->%d) CTS(%d->%d) RTS(%d->%d)", uart_num, // uart->_rxPin, rxPin, uart->_txPin, txPin, uart->_ctsPin, ctsPin, uart->_rtsPin, rtsPin); vTaskDelay(10); + // mute bus detaching callbacks to avoid terminating the UART driver when both RX and TX pins are detached + peripheral_bus_deinit_cb_t rxDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RX); + peripheral_bus_deinit_cb_t txDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_TX); + peripheral_bus_deinit_cb_t ctsDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_CTS); + peripheral_bus_deinit_cb_t rtsDeinit = perimanGetBusDeinit(ESP32_BUS_TYPE_UART_RTS); + perimanClearBusDeinit(ESP32_BUS_TYPE_UART_RX); + perimanClearBusDeinit(ESP32_BUS_TYPE_UART_TX); + perimanClearBusDeinit(ESP32_BUS_TYPE_UART_CTS); + perimanClearBusDeinit(ESP32_BUS_TYPE_UART_RTS); + // First step: detaches all previous UART pins bool rxPinChanged = rxPin >= 0 && rxPin != uart->_rxPin; if (rxPinChanged) { @@ -651,6 +711,21 @@ bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, in if (rtsPinChanged) { retCode &= _uartAttachPins(uart->num, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, rtsPin); } + + // restore bus detaching callbacks + if (rxDeinit != NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RX, rxDeinit); + } + if (txDeinit != NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_TX, txDeinit); + } + if (ctsDeinit != NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_CTS, ctsDeinit); + } + if (rtsDeinit != NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_UART_RTS, rtsDeinit); + } + UART_MUTEX_UNLOCK(); if (!retCode) { @@ -820,7 +895,7 @@ uart_t *uartBegin( uart_config.baud_rate = baudrate; #if SOC_UART_LP_NUM >= 1 if (uart_nr >= SOC_UART_HP_NUM) { // it is a LP UART NUM - if (uart->_uart_clock_source > 0) { + if (uart->_uart_clock_source >= 0) { uart_config.lp_source_clk = (soc_periph_lp_uart_clk_src_t)uart->_uart_clock_source; // use user defined LP UART clock log_v("Setting UART%d to user defined LP clock source (%d) ", uart_nr, uart->_uart_clock_source); } else { @@ -865,10 +940,20 @@ uart_t *uartBegin( if (retCode) { if (inverted) { // invert signal for both Rx and Tx - retCode &= ESP_OK == uart_set_line_inverse(uart_nr, UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV); + uint32_t _inv_mask = uart->inv_mask; + _inv_mask |= UART_SIGNAL_TXD_INV | UART_SIGNAL_RXD_INV; + retCode &= ESP_OK == uart_set_line_inverse(uart_nr, _inv_mask); + if (retCode) { + uart->inv_mask = _inv_mask; + log_v("UART%d: RX and TX signals are set to be inverted.", uart_nr); + } } else { // disable invert signal for both Rx and Tx retCode &= ESP_OK == uart_set_line_inverse(uart_nr, UART_SIGNAL_INV_DISABLE); + if (retCode) { + uart->inv_mask = UART_SIGNAL_INV_DISABLE; + log_v("UART%d: RX and TX signals are set not inverted.", uart_nr); + } } } // if all fine, set internal parameters @@ -881,14 +966,7 @@ uart_t *uartBegin( uart->_tx_buffer_size = tx_buffer_size; uart->has_peek = false; uart->peek_byte = 0; -#if SOC_UART_LP_NUM >= 1 - if (uart_nr >= SOC_UART_HP_NUM) { - uart->_uart_clock_source = uart_config.lp_source_clk; - } else -#endif - { - uart->_uart_clock_source = uart_config.source_clk; - } + // uart->_uart_clock_source can only change by explicit user API request/call } UART_MUTEX_UNLOCK(); @@ -974,31 +1052,64 @@ void uartEnd(uint8_t uart_num) { if (uart_is_driver_installed(uart_num)) { uart_driver_delete(uart_num); } + if (uartGetDebug() == uart_num) { + uartSetDebug(0); + } UART_MUTEX_UNLOCK(); } -void uartSetRxInvert(uart_t *uart, bool invert) { +// Helper generic function that takes a uart_signal_inv_t mask to be properly applied to the designated uart pin +// invMask can be UART_SIGNAL_RXD_INV, UART_SIGNAL_TXD_INV, UART_SIGNAL_RTS_INV, UART_SIGNAL_CTS_INV +// returns the operation success status +bool uartPinSignalInversion(uart_t *uart, uint32_t invMask, bool inverted) { if (uart == NULL) { - return; + return false; } -#if CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 - // POTENTIAL ISSUE :: original code only set/reset rxd_inv bit - // IDF or LL set/reset the whole inv_mask! - // if (invert) - // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_RXD_INV)); - // else - // ESP_ERROR_CHECK(uart_set_line_inverse(uart->num, UART_SIGNAL_INV_DISABLE)); - log_e("uartSetRxInvert is not supported in ESP32C6, ESP32H2 and ESP32P4"); -#else - // this implementation is better over IDF API because it only affects RXD - // this is supported in ESP32, ESP32-S2 and ESP32-C3 - uart_dev_t *hw = UART_LL_GET_HW(uart->num); - if (invert) { - hw->conf0.rxd_inv = 1; + UART_MUTEX_LOCK(); + uint32_t _inv_mask = uart->inv_mask; + if (inverted) { + _inv_mask |= invMask; } else { - hw->conf0.rxd_inv = 0; + _inv_mask &= ~invMask; } -#endif + bool retCode = ESP_OK == uart_set_line_inverse(uart->num, _inv_mask); + if (retCode) { + uart->inv_mask = _inv_mask; + } + UART_MUTEX_UNLOCK(); + return retCode; +} + +bool uartSetRxInvert(uart_t *uart, bool invert) { + if (uartPinSignalInversion(uart, UART_SIGNAL_RXD_INV, invert)) { + log_v("UART%d: RX signal inversion %s", uart->num, invert ? "enabled" : "disabled"); + return true; + } + return false; +} + +bool uartSetTxInvert(uart_t *uart, bool invert) { + if (uartPinSignalInversion(uart, UART_SIGNAL_TXD_INV, invert)) { + log_v("UART%d: TX signal inversion %s", uart->num, invert ? "enabled" : "disabled"); + return true; + } + return false; +} + +bool uartSetCtsInvert(uart_t *uart, bool invert) { + if (uartPinSignalInversion(uart, UART_SIGNAL_CTS_INV, invert)) { + log_v("UART%d: CTS signal inversion %s", uart->num, invert ? "enabled" : "disabled"); + return true; + } + return false; +} + +bool uartSetRtsInvert(uart_t *uart, bool invert) { + if (uartPinSignalInversion(uart, UART_SIGNAL_RTS_INV, invert)) { + log_v("UART%d: RTS signal inversion %s", uart->num, invert ? "enabled" : "disabled"); + return true; + } + return false; } uint32_t uartAvailable(uart_t *uart) { @@ -1149,10 +1260,9 @@ bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate) { } bool retCode = true; soc_module_clk_t newClkSrc = UART_SCLK_DEFAULT; - int8_t previousClkSrc = uart->_uart_clock_source; #if SOC_UART_LP_NUM >= 1 if (uart->num >= SOC_UART_HP_NUM) { // it is a LP UART NUM - if (uart->_uart_clock_source > 0) { + if (uart->_uart_clock_source >= 0) { newClkSrc = (soc_periph_lp_uart_clk_src_t)uart->_uart_clock_source; // use user defined LP UART clock log_v("Setting UART%d to user defined LP clock source (%d) ", uart->num, newClkSrc); } else { @@ -1187,13 +1297,10 @@ bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate) { } } UART_MUTEX_LOCK(); - // if necessary, set the correct UART Clock Source before changing the baudrate - if (previousClkSrc < 0 || previousClkSrc != newClkSrc) { - HP_UART_SRC_CLK_ATOMIC() { - uart_ll_set_sclk(UART_LL_GET_HW(uart->num), newClkSrc); - } - uart->_uart_clock_source = newClkSrc; + HP_UART_SRC_CLK_ATOMIC() { + uart_ll_set_sclk(UART_LL_GET_HW(uart->num), newClkSrc); } + // uart->_uart_clock_source can only change by explicit user API request/call if (uart_set_baudrate(uart->num, baud_rate) == ESP_OK) { log_v("Setting UART%d baud rate to %ld.", uart->num, baud_rate); uart->_baudrate = baud_rate; @@ -1312,7 +1419,7 @@ bool uartSetClockSource(uint8_t uartNum, uart_sclk_t clkSrc) { { uart->_uart_clock_source = clkSrc; } - //log_i("UART%d set clock source to %d", uart->num, uart->_uart_clock_source); + log_v("UART%d set clock source to %d", uart->num, uart->_uart_clock_source); return true; } diff --git a/cores/esp32/esp32-hal-uart.h b/cores/esp32/esp32-hal-uart.h index 41b005aa151..ab46a3c4f9c 100644 --- a/cores/esp32/esp32-hal-uart.h +++ b/cores/esp32/esp32-hal-uart.h @@ -61,7 +61,15 @@ void uartFlushTxOnly(uart_t *uart, bool txOnly); bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate); uint32_t uartGetBaudRate(uart_t *uart); -void uartSetRxInvert(uart_t *uart, bool invert); +// Helper generic function that takes a uart_signal_inv_t mask to be properly applied to the designated uart pin +// invMask can be UART_SIGNAL_RXD_INV, UART_SIGNAL_TXD_INV, UART_SIGNAL_RTS_INV, UART_SIGNAL_CTS_INV +// returns the operation success status +bool uartPinSignalInversion(uart_t *uart, uint32_t invMask, bool inverted); +// functions used to individually enable or disable UART pins inversion +bool uartSetRxInvert(uart_t *uart, bool invert); +bool uartSetTxInvert(uart_t *uart, bool invert); +bool uartSetCtsInvert(uart_t *uart, bool invert); +bool uartSetRtsInvert(uart_t *uart, bool invert); bool uartSetRxTimeout(uart_t *uart, uint8_t numSymbTimeout); bool uartSetRxFIFOFull(uart_t *uart, uint8_t numBytesFIFOFull); void uartSetFastReading(uart_t *uart); @@ -79,7 +87,6 @@ bool uartSetPins(uint8_t uart_num, int8_t rxPin, int8_t txPin, int8_t ctsPin, in // helper functions int8_t uart_get_RxPin(uint8_t uart_num); int8_t uart_get_TxPin(uint8_t uart_num); -void uart_init_PeriMan(void); // Enables or disables HW Flow Control function -- needs also to set CTS and/or RTS pins // UART_HW_FLOWCTRL_DISABLE = 0x0 disable hardware flow control diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 5ed99aeb205..e3a1f6e4dde 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -63,7 +63,8 @@ extern "C" { #if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 static const uint8_t BOOT_PIN = 0; -#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C61 +#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61 || CONFIG_IDF_TARGET_ESP32H2 \ + || CONFIG_IDF_TARGET_ESP32C61 static const uint8_t BOOT_PIN = 9; #elif CONFIG_IDF_TARGET_ESP32P4 static const uint8_t BOOT_PIN = 35; @@ -100,6 +101,7 @@ void yield(void); #include "esp32-hal-psram.h" #include "esp32-hal-rgb-led.h" #include "esp32-hal-cpu.h" +#include "esp32-hal-hosted.h" void analogWrite(uint8_t pin, int value); void analogWriteFrequency(uint8_t pin, uint32_t freq); diff --git a/cores/esp32/esp_arduino_version.h b/cores/esp32/esp_arduino_version.h index 120377c61f7..587fe02cd16 100644 --- a/cores/esp32/esp_arduino_version.h +++ b/cores/esp32/esp_arduino_version.h @@ -23,7 +23,7 @@ extern "C" { /** Minor version number (x.X.x) */ #define ESP_ARDUINO_VERSION_MINOR 3 /** Patch version number (x.x.X) */ -#define ESP_ARDUINO_VERSION_PATCH 0 +#define ESP_ARDUINO_VERSION_PATCH 4 /** * Macro to convert ARDUINO version number into an integer diff --git a/cores/esp32/main.cpp b/cores/esp32/main.cpp index 6c4d50a9a84..4d1ec770c07 100644 --- a/cores/esp32/main.cpp +++ b/cores/esp32/main.cpp @@ -44,17 +44,25 @@ __attribute__((weak)) bool shouldPrintChipDebugReport(void) { return false; } +// this function can be changed by the sketch using the macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) +__attribute__((weak)) uint64_t getArduinoSetupWaitTime_ms(void) { + return 0; +} + void loopTask(void *pvParameters) { -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) - // sets UART0 (default console) RX/TX pins as already configured in boot or as defined in variants/pins_arduino.h - Serial0.setPins(gpioNumberToDigitalPin(SOC_RX0), gpioNumberToDigitalPin(SOC_TX0)); -#endif + delay(getArduinoSetupWaitTime_ms()); #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG printBeforeSetupInfo(); #else if (shouldPrintChipDebugReport()) { printBeforeSetupInfo(); } +#endif +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) + // sets UART0 (default console) RX/TX pins as already configured in boot or as defined in variants/pins_arduino.h + Serial0.setPins(gpioNumberToDigitalPin(SOC_RX0), gpioNumberToDigitalPin(SOC_TX0)); + // time in ms that the sketch may wait before starting its execution - default is zero + // usually done for opening the Serial Monitor and seeing all debug messages #endif setup(); #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG diff --git a/cores/esp32/stdlib_noniso.c b/cores/esp32/stdlib_noniso.c index 0acb26c67b9..352eed4fc41 100644 --- a/cores/esp32/stdlib_noniso.c +++ b/cores/esp32/stdlib_noniso.c @@ -49,7 +49,7 @@ char *ltoa(long value, char *result, int base) { } char *out = result; - long quotient = abs(value); + long quotient = labs(value); do { const long tmp = quotient / base; diff --git a/docs/_static/chatbot_widget.css b/docs/_static/chatbot_widget.css new file mode 100644 index 00000000000..c6585b0588d --- /dev/null +++ b/docs/_static/chatbot_widget.css @@ -0,0 +1,9 @@ +#kapa-widget-container { + z-index: 10000 !important; + position: absolute !important; + } + + .mantine-Modal-root { + z-index: 10000; + position: absolute; + } diff --git a/docs/_static/chatbot_widget_en.js b/docs/_static/chatbot_widget_en.js new file mode 100644 index 00000000000..2b083966803 --- /dev/null +++ b/docs/_static/chatbot_widget_en.js @@ -0,0 +1,30 @@ +document.addEventListener("DOMContentLoaded", function () { + var script = document.createElement("script"); + script.src = "https://widget.kapa.ai/kapa-widget.bundle.js"; + script.setAttribute("data-bot-protection-mechanism", "hcaptcha"); + script.setAttribute("data-website-id", "f67ff377-ba84-4009-aceb-5e582755abad"); + script.setAttribute("data-project-name", "ESP32 Arduino Core Documentation"); + script.setAttribute("data-project-color", "#C62817"); + script.setAttribute("data-project-logo", "https://dl.espressif.com/public/logo.png"); + script.setAttribute("data-button-image", "https://dl.espressif.com/chatbot/Chatbot.png"); + script.setAttribute("data-button-text-font-size", "0px"); + script.setAttribute("data-button-border-radius", "50%"); + script.setAttribute("data-button-bg-color", "#38393a"); + script.setAttribute("data-button-border", "#38393a"); + script.setAttribute("data-button-height", "45px"); + script.setAttribute("data-button-width", "45px"); + script.setAttribute("data-button-animation-enabled", "false"); + script.setAttribute("data-button-image-height", "100%"); + script.setAttribute("data-button-image-width", "100%"); + script.setAttribute("data-button-padding", "0"); + script.setAttribute("data-button-hover-animation-enabled", "false"); + script.setAttribute("data-button-position-top", "50px"); + script.setAttribute("data-button-position-left", "305px"); + script.setAttribute("data-button-box-shadow", "0px 6px 12px 1px rgba(0,0,0,0.16)"); + script.setAttribute("data-modal-override-open-class", "test-ai"); + script.setAttribute("data-user-analytics-fingerprint-enabled", "true"); + script.setAttribute("data-modal-disclaimer", "This custom large language model (LLM), trained on official documentation from espressif.com, is designed to provide technical support and answers related to Espressif’s products and services. Give it a try, share your thoughts, and let us know your feedback—we truly appreciate it! \n\n**Note**: AI-generated information may be incomplete or inaccurate. Always verify critical information with official sources."); + script.setAttribute("data-modal-example-questions", "What is the ESP32 Arduino Core?,How do I get started with the ESP32 Arduino Core?"); + script.async = true; + document.head.appendChild(script); + }); diff --git a/docs/conf_common.py b/docs/conf_common.py index 10d4bd715b2..0960d7c4315 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -4,7 +4,7 @@ # Used for substituting variables in the documentation rst_prolog = """ -.. |version| replace:: 3.3.0 +.. |version| replace:: 3.3.4 .. |idf_version| replace:: 5.5 """ @@ -28,6 +28,9 @@ html_static_path = ["../_static"] +html_js_files = ["../_static/chatbot_widget_en.js"] +html_css_files = ["../_static/chatbot_widget.css"] + # Conditional content extensions += [ # noqa: F405 diff --git a/docs/en/api/adc.rst b/docs/en/api/adc.rst index f2245cc1e5d..a0c2677854f 100644 --- a/docs/en/api/adc.rst +++ b/docs/en/api/adc.rst @@ -162,7 +162,7 @@ ADC Continuous mode is an API designed for performing analog conversions on mult with the feature of receiving a callback upon completion of these conversions to access the results. This API allows you to specify the desired number of conversions per pin within a single cycle, along with its corresponding sampling rate. -The outcome of the ``analogContinuousRead`` function is an array of ``adc_continuous_data_t`` structures. +The outcome of the ``analogContinuousRead`` function is an array of ``adc_continuous_result_t`` structures. These structures hold both the raw average value and the average value in millivolts for each pin. analogContinuous @@ -186,7 +186,7 @@ If ``false`` is returned, error occurs and ADC continuous was not configured. analogContinuousRead ^^^^^^^^^^^^^^^^^^^^ -This function is used to read ADC continuous data to the result buffer. The result buffer is an array of ``adc_continuous_data_t``. +This function is used to read ADC continuous data to the result buffer. The result buffer is an array of ``adc_continuous_result_t``. .. code-block:: arduino @@ -195,13 +195,13 @@ This function is used to read ADC continuous data to the result buffer. The resu uint8_t channel; /*!impl_name()); + } + +Hostname Management +******************* + +.. code-block:: arduino + + static const char *getHostname(); + static bool setHostname(const char *hostname); + static bool hostname(const String &aHostname); + +Gets or sets the system hostname. The hostname is used for network identification and mDNS. + +**Example:** + +.. code-block:: arduino + + // Set hostname + Network.setHostname("my-esp32-device"); + + // Get hostname + const char *hostname = Network.getHostname(); + Serial.println(hostname); + +DNS Resolution +************** + +.. code-block:: arduino + + int hostByName(const char *aHostname, IPAddress &aResult); + +Resolves a hostname to an IP address using DNS. Returns ``1`` on success, error code on failure. + +**Example:** + +.. code-block:: arduino + + IPAddress ip; + if (Network.hostByName("www.example.com", ip) == 1) { + Serial.print("Resolved IP: "); + Serial.println(ip); + } + +MAC Address +*********** + +.. code-block:: arduino + + uint8_t *macAddress(uint8_t *mac); + String macAddress(); + +Gets the MAC address of the default network interface. + +**Example:** + +.. code-block:: arduino + + uint8_t mac[6]; + Network.macAddress(mac); + Serial.print("MAC: "); + for (int i = 0; i < 6; i++) { + if (i > 0) Serial.print(":"); + Serial.print(mac[i], HEX); + } + Serial.println(); + + // Or as String + String macStr = Network.macAddress(); + Serial.println(macStr); + +Network Events +-------------- + +The ``NetworkEvents`` class provides a centralized event handling system for all network-related events. +``NetworkManager`` extends ``NetworkEvents``, so you can register event callbacks directly on the ``Network`` object. + +Event Types +*********** + +Network events are defined in ``arduino_event_id_t`` enum: + +**Ethernet Events:** +* ``ARDUINO_EVENT_ETH_START`` +* ``ARDUINO_EVENT_ETH_STOP`` +* ``ARDUINO_EVENT_ETH_CONNECTED`` +* ``ARDUINO_EVENT_ETH_DISCONNECTED`` +* ``ARDUINO_EVENT_ETH_GOT_IP`` +* ``ARDUINO_EVENT_ETH_LOST_IP`` +* ``ARDUINO_EVENT_ETH_GOT_IP6`` + +**Wi-Fi Station Events:** +* ``ARDUINO_EVENT_WIFI_STA_START`` +* ``ARDUINO_EVENT_WIFI_STA_STOP`` +* ``ARDUINO_EVENT_WIFI_STA_CONNECTED`` +* ``ARDUINO_EVENT_WIFI_STA_DISCONNECTED`` +* ``ARDUINO_EVENT_WIFI_STA_GOT_IP`` +* ``ARDUINO_EVENT_WIFI_STA_LOST_IP`` +* ``ARDUINO_EVENT_WIFI_STA_GOT_IP6`` + +**Wi-Fi AP Events:** +* ``ARDUINO_EVENT_WIFI_AP_START`` +* ``ARDUINO_EVENT_WIFI_AP_STOP`` +* ``ARDUINO_EVENT_WIFI_AP_STACONNECTED`` +* ``ARDUINO_EVENT_WIFI_AP_STADISCONNECTED`` +* ``ARDUINO_EVENT_WIFI_AP_STAIPASSIGNED`` +* ``ARDUINO_EVENT_WIFI_AP_GOT_IP6`` + +**PPP Events:** +* ``ARDUINO_EVENT_PPP_START`` +* ``ARDUINO_EVENT_PPP_STOP`` +* ``ARDUINO_EVENT_PPP_CONNECTED`` +* ``ARDUINO_EVENT_PPP_DISCONNECTED`` +* ``ARDUINO_EVENT_PPP_GOT_IP`` +* ``ARDUINO_EVENT_PPP_LOST_IP`` +* ``ARDUINO_EVENT_PPP_GOT_IP6`` + +Registering Event Callbacks +**************************** + +Three types of callback functions are supported: + +**1. Simple Event Callback (Event ID only):** + +.. code-block:: arduino + + typedef void (*NetworkEventCb)(arduino_event_id_t event); + network_event_handle_t onEvent(NetworkEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + +**2. Functional Callback (Event ID and Info):** + +.. code-block:: arduino + + typedef std::function NetworkEventFuncCb; + network_event_handle_t onEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + +**3. System Callback (Event Structure):** + +.. code-block:: arduino + + typedef void (*NetworkEventSysCb)(arduino_event_t *event); + network_event_handle_t onEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + +**Example - Simple Callback:** + +.. code-block:: arduino + + void onNetworkEvent(arduino_event_id_t event) { + Serial.print("Network event: "); + Serial.println(NetworkEvents::eventName(event)); + + if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { + Serial.println("WiFi connected!"); + } + } + + void setup() { + Network.begin(); + Network.onEvent(onNetworkEvent); + } + +**Example - Functional Callback with Event Info:** + +.. code-block:: arduino + + void setup() { + Network.begin(); + + Network.onEvent([](arduino_event_id_t event, arduino_event_info_t info) { + if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { + Serial.print("IP Address: "); + Serial.println(IPAddress(info.got_ip.ip_info.ip.addr)); + Serial.print("Gateway: "); + Serial.println(IPAddress(info.got_ip.ip_info.gw.addr)); + } + }); + } + +**Example - System Callback:** + +.. code-block:: arduino + + void onNetworkEventSys(arduino_event_t *event) { + Serial.print("Event: "); + Serial.println(NetworkEvents::eventName(event->event_id)); + + if (event->event_id == ARDUINO_EVENT_WIFI_STA_GOT_IP) { + IPAddress ip = IPAddress(event->event_info.got_ip.ip_info.ip.addr); + Serial.print("Got IP: "); + Serial.println(ip); + } + } + + void setup() { + Network.begin(); + Network.onEvent(onNetworkEventSys); + } + +Removing Event Callbacks +************************ + +.. code-block:: arduino + + void removeEvent(NetworkEventCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + void removeEvent(NetworkEventFuncCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + void removeEvent(NetworkEventSysCb cbEvent, arduino_event_id_t event = ARDUINO_EVENT_MAX); + void removeEvent(network_event_handle_t event_handle); + +Remove event callbacks by function pointer or by handle (recommended). + +**Example:** + +.. code-block:: arduino + + network_event_handle_t eventHandle; + + void setup() { + Network.begin(); + // Register and save handle + eventHandle = Network.onEvent(onNetworkEvent); + } + + void loop() { + // Later, remove by handle + Network.removeEvent(eventHandle); + } + +Event Information +***************** + +.. code-block:: arduino + + static const char *eventName(arduino_event_id_t id); + +Returns a human-readable name for an event ID. + +**Example:** + +.. code-block:: arduino + + Serial.println(NetworkEvents::eventName(ARDUINO_EVENT_WIFI_STA_GOT_IP)); + // Output: "WIFI_STA_GOT_IP" + +Network Interface Base Class +----------------------------- + +The ``NetworkInterface`` class is the base class for all network interfaces (Wi-Fi STA, Wi-Fi AP, Ethernet, PPP). +It provides common functionality for IP configuration, status checking, and network information retrieval. + +All network interfaces inherit from ``NetworkInterface`` and can be used polymorphically. + +IP Configuration +**************** + +.. code-block:: arduino + + bool config( + IPAddress local_ip = (uint32_t)0x00000000, + IPAddress gateway = (uint32_t)0x00000000, + IPAddress subnet = (uint32_t)0x00000000, + IPAddress dns1 = (uint32_t)0x00000000, + IPAddress dns2 = (uint32_t)0x00000000, + IPAddress dns3 = (uint32_t)0x00000000 + ); + bool dnsIP(uint8_t dns_no, IPAddress ip); + +Configures static IP address, gateway, subnet mask, and DNS servers. +For server interfaces (Wi-Fi AP), ``dns1`` is the DHCP lease range start and ``dns2`` is the DNS server. + +**Example:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + + void setup() { + Network.begin(); + + // Configure static IP + IPAddress local_ip(192, 168, 1, 100); + IPAddress gateway(192, 168, 1, 1); + IPAddress subnet(255, 255, 255, 0); + IPAddress dns1(8, 8, 8, 8); + IPAddress dns2(8, 8, 4, 4); + + WiFi.STA.begin(); + WiFi.STA.config(local_ip, gateway, subnet, dns1, dns2); + WiFi.STA.connect("ssid", "password"); + } + +Hostname +******** + +.. code-block:: arduino + + const char *getHostname() const; + bool setHostname(const char *hostname) const; + +Gets or sets the hostname for the specific interface. + +**Example:** + +.. code-block:: arduino + + WiFi.STA.setHostname("my-wifi-device"); + Serial.println(WiFi.STA.getHostname()); + +Status Checking +*************** + +.. code-block:: arduino + + bool started() const; + bool connected() const; + bool hasIP() const; + bool hasLinkLocalIPv6() const; + bool hasGlobalIPv6() const; + bool linkUp() const; + +Check the status of the network interface. + +**Example:** + +.. code-block:: arduino + + if (WiFi.STA.started()) { + Serial.println("WiFi interface started"); + } + + if (WiFi.STA.connected()) { + Serial.println("WiFi connected"); + } + + if (WiFi.STA.hasIP()) { + Serial.println("WiFi has IP address"); + } + +IPv6 Support +************ + +.. code-block:: arduino + + bool enableIPv6(bool en = true); + +Enables or disables IPv6 support on the interface. + +**Example:** + +.. code-block:: arduino + + WiFi.STA.enableIPv6(true); + + if (WiFi.STA.hasGlobalIPv6()) { + IPAddress ipv6 = WiFi.STA.globalIPv6(); + Serial.print("Global IPv6: "); + Serial.println(ipv6); + } + +IP Address Information +********************** + +.. code-block:: arduino + + IPAddress localIP() const; + IPAddress subnetMask() const; + IPAddress gatewayIP() const; + IPAddress dnsIP(uint8_t dns_no = 0) const; + IPAddress broadcastIP() const; + IPAddress networkID() const; + uint8_t subnetCIDR() const; + IPAddress linkLocalIPv6() const; // IPv6 only + IPAddress globalIPv6() const; // IPv6 only + +Get IP address information from the interface. + +**Example:** + +.. code-block:: arduino + + Serial.print("Local IP: "); + Serial.println(WiFi.STA.localIP()); + + Serial.print("Subnet Mask: "); + Serial.println(WiFi.STA.subnetMask()); + + Serial.print("Gateway: "); + Serial.println(WiFi.STA.gatewayIP()); + + Serial.print("DNS: "); + Serial.println(WiFi.STA.dnsIP()); + + Serial.print("Broadcast: "); + Serial.println(WiFi.STA.broadcastIP()); + + Serial.print("Network ID: "); + Serial.println(WiFi.STA.networkID()); + + Serial.print("Subnet CIDR: /"); + Serial.println(WiFi.STA.subnetCIDR()); + +MAC Address +*********** + +.. code-block:: arduino + + uint8_t *macAddress(uint8_t *mac) const; + String macAddress() const; + +Get the MAC address of the interface. + +**Example:** + +.. code-block:: arduino + + uint8_t mac[6]; + WiFi.STA.macAddress(mac); + Serial.print("MAC: "); + for (int i = 0; i < 6; i++) { + if (i > 0) Serial.print(":"); + Serial.print(mac[i], HEX); + } + Serial.println(); + + // Or as String + Serial.println(WiFi.STA.macAddress()); + +Default Interface +***************** + +.. code-block:: arduino + + bool setDefault(); + bool isDefault() const; + +Set this interface as the default network interface, or check if it is the default. + +**Example:** + +.. code-block:: arduino + + // Set WiFi STA as default + WiFi.STA.setDefault(); + + // Check if it's default + if (WiFi.STA.isDefault()) { + Serial.println("WiFi STA is the default interface"); + } + +Route Priority +*************** + +.. code-block:: arduino + + int getRoutePrio() const; + int setRoutePrio(int prio); // ESP-IDF 5.5+ + +Gets or sets the route priority for the interface. Higher priority interfaces are preferred for routing. + +**Example:** + +.. code-block:: arduino + + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + // Set higher priority for Ethernet + ETH.setRoutePrio(100); + WiFi.STA.setRoutePrio(50); + #endif + +Status Bits and Waiting +*********************** + +.. code-block:: arduino + + int getStatusBits() const; + int waitStatusBits(int bits, uint32_t timeout_ms) const; + +Get current status bits or wait for specific status bits to be set. + +**Status Bits:** +* ``ESP_NETIF_STARTED_BIT``: Interface has been started +* ``ESP_NETIF_CONNECTED_BIT``: Interface is connected +* ``ESP_NETIF_HAS_IP_BIT``: Interface has an IP address +* ``ESP_NETIF_HAS_LOCAL_IP6_BIT``: Interface has link-local IPv6 +* ``ESP_NETIF_HAS_GLOBAL_IP6_BIT``: Interface has global IPv6 +* ``ESP_NETIF_WANT_IP6_BIT``: Interface wants IPv6 +* ``ESP_NETIF_HAS_STATIC_IP_BIT``: Interface has static IP configuration + +**Example:** + +.. code-block:: arduino + + // Wait for WiFi STA to get IP address (with 10 second timeout) + if (WiFi.STA.waitStatusBits(ESP_NETIF_HAS_IP_BIT, 10000) & ESP_NETIF_HAS_IP_BIT) { + Serial.println("WiFi STA got IP address!"); + } else { + Serial.println("Timeout waiting for IP address"); + } + +Interface Information +********************* + +.. code-block:: arduino + + const char *ifkey() const; + const char *desc() const; + String impl_name() const; + int impl_index() const; + esp_netif_t *netif(); + +Get interface identification and description information. + +**Example:** + +.. code-block:: arduino + + Serial.print("Interface key: "); + Serial.println(WiFi.STA.ifkey()); + + Serial.print("Description: "); + Serial.println(WiFi.STA.desc()); + + Serial.print("Implementation name: "); + Serial.println(WiFi.STA.impl_name()); + + Serial.print("Implementation index: "); + Serial.println(WiFi.STA.impl_index()); + +Network Interface Implementations +---------------------------------- + +Wi-Fi Station (STA) +******************** + +The ``STAClass`` (accessed via ``WiFi.STA`` object) extends ``NetworkInterface`` and provides Wi-Fi Station (client) mode functionality. + +**Key Features:** +* Connect to Wi-Fi access points +* Automatic reconnection +* WPA2/WPA3 security support +* Enterprise Wi-Fi support (WPA2-Enterprise) +* Scanning for available networks + +**Example:** + +.. code-block:: arduino + + #include "WiFi.h" + + void setup() { + Network.begin(); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + + while (WiFi.STA.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println(); + Serial.print("Connected! IP: "); + Serial.println(WiFi.STA.localIP()); + } + +For detailed Wi-Fi Station API documentation, see :doc:`wifi`. + +Wi-Fi Access Point (AP) +*********************** + +The ``APClass`` (accessed via ``WiFi.AP`` object) extends ``NetworkInterface`` and provides Wi-Fi Access Point mode functionality. + +**Key Features:** +* Create Wi-Fi access points +* DHCP server for connected stations +* Network Address Translation (NAT) +* Captive portal support + +**Example:** + +.. code-block:: arduino + + #include "WiFi.h" + + void setup() { + Network.begin(); + WiFi.AP.begin(); + WiFi.AP.create("MyESP32AP", "password123"); + + Serial.print("AP IP: "); + Serial.println(WiFi.AP.localIP()); + } + +For detailed Wi-Fi AP API documentation, see :doc:`wifi`. + +Ethernet +******** + +The ``ETHClass`` (accessed via ``ETH`` object) extends ``NetworkInterface`` and provides Ethernet connectivity. + +**Key Features:** +* Support for multiple Ethernet PHY types +* SPI-based and EMAC-based Ethernet controllers +* Automatic link detection +* Full-duplex operation + +**Example:** + +.. code-block:: arduino + + #include "ETH.h" + + void setup() { + Network.begin(); + ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_POWER, ETH_CLK_MODE); + + while (!ETH.hasIP()) { + delay(500); + } + + Serial.print("Ethernet IP: "); + Serial.println(ETH.localIP()); + } + +For detailed Ethernet API documentation, see :doc:`ethernet`. + +PPP (Point-to-Point Protocol) +******************************* + +The ``PPPClass`` (accessed via ``PPP`` object) extends ``NetworkInterface`` and provides PPP connectivity, typically used with cellular modems. + +**Key Features:** +* Cellular modem support (SIM7000, SIM7600, BG96, etc.) +* APN configuration +* PIN code support +* Hardware flow control + +**Example:** + +.. code-block:: arduino + + #include "PPP.h" + + void setup() { + Network.begin(); + PPP.begin(PPP_MODEM_SIM7600, 1, 115200); + PPP.setApn("your.apn.here"); + + while (!PPP.hasIP()) { + delay(500); + } + + Serial.print("PPP IP: "); + Serial.println(PPP.localIP()); + } + +For detailed PPP API documentation, see the PPP library documentation. + +Network Communication Classes +------------------------------ + +The Network library provides unified communication classes that work with any network interface: +``NetworkClient``, ``NetworkServer``, and ``NetworkUdp``. + +NetworkClient +************* + +The ``NetworkClient`` class provides TCP client functionality that works with any network interface. + +**Key Features:** +* Connect to TCP servers +* Read/write data +* Timeout configuration +* Socket options + +**Example:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + + NetworkClient client; + + void setup() { + Network.begin(); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + while (WiFi.STA.status() != WL_CONNECTED) delay(500); + + if (client.connect("www.example.com", 80)) { + client.println("GET / HTTP/1.1"); + client.println("Host: www.example.com"); + client.println(); + + while (client.available()) { + char c = client.read(); + Serial.print(c); + } + + client.stop(); + } + } + +NetworkServer +************* + +The ``NetworkServer`` class provides TCP server functionality that works with any network interface. + +**Key Features:** +* Accept incoming TCP connections +* Multiple client support +* Timeout configuration + +**Example:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + + NetworkServer server(80); + + void setup() { + Network.begin(); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + while (WiFi.STA.status() != WL_CONNECTED) delay(500); + + server.begin(); + Serial.print("Server started on: "); + Serial.println(WiFi.STA.localIP()); + } + + void loop() { + NetworkClient client = server.accept(); + if (client) { + Serial.println("New client connected"); + client.println("Hello from ESP32!"); + client.stop(); + } + } + +NetworkUdp +********** + +The ``NetworkUdp`` class provides UDP communication that works with any network interface. + +**Key Features:** +* Send/receive UDP packets +* Multicast support +* Remote IP and port information + +**Example:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + + NetworkUDP udp; + + void setup() { + Network.begin(); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + while (WiFi.STA.status() != WL_CONNECTED) delay(500); + + udp.begin(1234); + } + + void loop() { + int packetSize = udp.parsePacket(); + if (packetSize) { + Serial.print("Received packet from: "); + Serial.print(udp.remoteIP()); + Serial.print(":"); + Serial.println(udp.remotePort()); + + char buffer[255]; + int len = udp.read(buffer, 255); + if (len > 0) { + buffer[len] = 0; + Serial.println(buffer); + } + } + } + +Multiple Interface Management +----------------------------- + +The Network Manager allows you to manage multiple network interfaces simultaneously and switch between them as needed. + +**Example - Multiple Interfaces:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + #include "ETH.h" + + void setup() { + Network.begin(); + + // Start Ethernet + ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC, ETH_PHY_MDIO, ETH_PHY_POWER, ETH_CLK_MODE); + + // Start WiFi as backup + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + + // Wait for either interface to connect + while (!ETH.connected() && WiFi.STA.status() != WL_CONNECTED) { + delay(500); + } + + // Set the connected interface as default + if (ETH.connected()) { + Network.setDefaultInterface(ETH); + Serial.println("Using Ethernet"); + } else if (WiFi.STA.status() == WL_CONNECTED) { + Network.setDefaultInterface(WiFi.STA); + Serial.println("Using WiFi"); + } + } + +**Example - Interface Priority:** + +.. code-block:: arduino + + void setup() { + Network.begin(); + + // Start both interfaces + ETH.begin(...); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + + // Set route priorities (ESP-IDF 5.5+) + #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) + ETH.setRoutePrio(100); // Higher priority + WiFi.STA.setRoutePrio(50); // Lower priority + #endif + + // Ethernet will be preferred for routing when both are connected + } + +Event Handling Examples +----------------------- + +**Example - Monitor All Network Events:** + +.. code-block:: arduino + + #include "Network.h" + + void onNetworkEvent(arduino_event_id_t event) { + Serial.print("[Network Event] "); + Serial.println(NetworkEvents::eventName(event)); + + switch (event) { + case ARDUINO_EVENT_WIFI_STA_GOT_IP: + Serial.println("WiFi Station got IP!"); + break; + case ARDUINO_EVENT_ETH_GOT_IP: + Serial.println("Ethernet got IP!"); + break; + case ARDUINO_EVENT_PPP_GOT_IP: + Serial.println("PPP got IP!"); + break; + default: + break; + } + } + + void setup() { + Serial.begin(115200); + Network.begin(); + Network.onEvent(onNetworkEvent); + } + +**Example - Interface-Specific Event Handling:** + +.. code-block:: arduino + + #include "Network.h" + #include "WiFi.h" + + void onWiFiEvent(arduino_event_id_t event, arduino_event_info_t info) { + if (event == ARDUINO_EVENT_WIFI_STA_GOT_IP) { + IPAddress ip = IPAddress(info.got_ip.ip_info.ip.addr); + IPAddress gateway = IPAddress(info.got_ip.ip_info.gw.addr); + IPAddress subnet = IPAddress(info.got_ip.ip_info.netmask.addr); + + Serial.println("WiFi Connected!"); + Serial.print("IP: "); + Serial.println(ip); + Serial.print("Gateway: "); + Serial.println(gateway); + Serial.print("Subnet: "); + Serial.println(subnet); + } + } + + void setup() { + Network.begin(); + Network.onEvent(onWiFiEvent, ARDUINO_EVENT_WIFI_STA_GOT_IP); + WiFi.STA.begin(); + WiFi.STA.connect("ssid", "password"); + } + +Troubleshooting +--------------- + +**Interface Not Starting:** +* Ensure ``Network.begin()`` is called before using any interface +* Check that the interface-specific initialization is correct +* Verify hardware connections (for Ethernet/PPP) + +**Default Interface Not Working:** +* Explicitly set the default interface using ``Network.setDefaultInterface()`` +* Check interface status using ``started()``, ``connected()``, and ``hasIP()`` +* Verify route priorities if using multiple interfaces + +**Events Not Firing:** +* Ensure ``Network.begin()`` is called to initialize the event system +* Check that callbacks are registered before the events occur +* Use ``NetworkEvents::eventName()`` to verify event IDs + +**IP Configuration Issues:** +* For static IP, ensure all parameters (IP, gateway, subnet) are provided +* Check that the IP address is not already in use on the network +* Verify DNS server addresses are correct + +**Multiple Interface Conflicts:** +* Set appropriate route priorities to control which interface is used +* Use ``setDefaultInterface()`` to explicitly select the default +* Monitor events to see which interface gets IP addresses first + +Related Documentation +--------------------- + +* `ESP-IDF Network Interface Documentation `_ diff --git a/docs/en/api/rmt.rst b/docs/en/api/rmt.rst index 6f87054a9c2..84d885c7d90 100644 --- a/docs/en/api/rmt.rst +++ b/docs/en/api/rmt.rst @@ -1,24 +1,454 @@ -### -RMT -### +#################### +Remote Control (RMT) +#################### About ----- +The Remote Control Transceiver (RMT) peripheral was originally designed to act as an infrared transceiver, +but it can be used to generate or receive many other types of digital signals with precise timing. -.. note:: This is a work in progress project and this section is still missing. If you want to contribute, please see the `Contributions Guide <../contributing.html>`_. +The RMT peripheral is capable of transmitting and receiving digital signals with precise timing control, +making it ideal for protocols that require specific pulse widths and timing, such as: -Remote Control Transceiver (RMT) peripheral was designed to act as an infrared transceiver. +* **Infrared (IR) remote control protocols** (NEC, RC5, Sony, etc.) +* **WS2812/NeoPixel RGB LED control** (requires precise timing) +* **Custom communication protocols** with specific timing requirements +* **Pulse generation** for various applications -Example -------- +RMT operates by encoding digital signals as sequences of high/low pulses with specific durations. +Each RMT symbol represents two consecutive pulses (level0/duration0 and level1/duration1). -To get started with RMT, you can try: +RMT Memory Blocks +----------------- -RMT Write RGB LED +RMT channels use memory blocks to store signal data. The number of available memory blocks varies by SoC: + +========= ================== ====================================== +ESP32 SoC Memory Blocks Notes +========= ================== ====================================== +ESP32 8 blocks total Shared between TX and RX channels +ESP32-S2 4 blocks total Shared between TX and RX channels +ESP32-S3 4 blocks TX + 4 RX Separate memory for TX and RX channels +ESP32-C3 2 blocks TX + 2 RX Separate memory for TX and RX channels +ESP32-C5 2 blocks TX + 2 RX Separate memory for TX and RX channels +ESP32-C6 2 blocks TX + 2 RX Separate memory for TX and RX channels +ESP32-H2 2 blocks TX + 2 RX Separate memory for TX and RX channels +ESP32-P4 4 blocks TX + 4 RX Separate memory for TX and RX channels +========= ================== ====================================== + +Each memory block can store ``RMT_SYMBOLS_PER_CHANNEL_BLOCK`` symbols (64 for ESP32/ESP32-S2, 48 for ESP32-S3/ESP32-C3/ESP32-C5/ESP32-C6/ESP32-H2/ESP32-P4). + +**Note:** Each RMT symbol is 4 bytes (32 bits), containing two pulses with their durations and levels. + +Arduino-ESP32 RMT API +--------------------- + +rmtInit +******* + +Initializes an RMT channel for a specific GPIO pin with the specified direction, memory size, and frequency. + +.. code-block:: arduino + + bool rmtInit(int pin, rmt_ch_dir_t channel_direction, rmt_reserve_memsize_t memsize, uint32_t frequency_Hz); + +* ``pin`` - GPIO pin number to use for RMT +* ``channel_direction`` - Channel direction: + + * ``RMT_RX_MODE`` - Receive mode (for reading signals) + * ``RMT_TX_MODE`` - Transmit mode (for sending signals) + +* ``memsize`` - Number of memory blocks to reserve for this channel: + + * ``RMT_MEM_NUM_BLOCKS_1`` - 1 block + * ``RMT_MEM_NUM_BLOCKS_2`` - 2 blocks + * ``RMT_MEM_NUM_BLOCKS_3`` - 3 blocks (ESP32 only) + * ``RMT_MEM_NUM_BLOCKS_4`` - 4 blocks (ESP32 only) + * ``RMT_MEM_NUM_BLOCKS_5`` through ``RMT_MEM_NUM_BLOCKS_8`` - 5-8 blocks (ESP32 only) + +* ``frequency_Hz`` - RMT channel frequency in Hz (tick frequency). Must be between 312.5 kHz and 80 MHz. + + The frequency determines the resolution of pulse durations. For example: + + * 10 MHz (100 ns tick) - High precision, suitable for WS2812 LEDs + * 1 MHz (1 µs tick) - Good for most IR protocols + * 400 kHz (2.5 µs tick) - Suitable for slower protocols + +This function returns ``true`` if initialization is successful, ``false`` otherwise. + +**Note:** The RMT tick is set by the frequency parameter. Example: 100 ns tick => 10 MHz, thus frequency will be 10,000,000 Hz. + +rmtDeinit +********* + +Deinitializes the RMT channel and releases all allocated resources for the specified pin. + +.. code-block:: arduino + + bool rmtDeinit(int pin); + +* ``pin`` - GPIO pin number that was initialized with RMT + +This function returns ``true`` if deinitialization is successful, ``false`` otherwise. + +rmtSetEOT +********* + +Sets the End of Transmission (EOT) level for the RMT pin when transmission ends. +This function affects how ``rmtWrite()``, ``rmtWriteAsync()``, or ``rmtWriteLooping()`` will set the pin after writing the data. + +.. code-block:: arduino + + bool rmtSetEOT(int pin, uint8_t EOT_Level); + +* ``pin`` - GPIO pin number configured for RMT TX mode +* ``EOT_Level`` - End of transmission level: + + * ``0`` (LOW) - Pin will be set to LOW after transmission (default) + * Non-zero (HIGH) - Pin will be set to HIGH after transmission + +**Note:** This only affects the transmission process. The pre-transmission idle level can be set manually using ``digitalWrite(pin, level)``. + +This function returns ``true`` if EOT level is set successfully, ``false`` otherwise. + +rmtWrite +******** + +Sends RMT data in blocking mode. The function waits until all data is transmitted or until timeout occurs. + +.. code-block:: arduino + + bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms); + +* ``pin`` - GPIO pin number configured for RMT TX mode +* ``data`` - Pointer to array of ``rmt_data_t`` symbols to transmit +* ``num_rmt_symbols`` - Number of RMT symbols to transmit +* ``timeout_ms`` - Timeout in milliseconds. Use ``RMT_WAIT_FOR_EVER`` for indefinite wait + +**Blocking mode:** The function only returns after sending all data or by timeout. + +This function returns ``true`` if transmission is successful, ``false`` on error or timeout. + +**Example:** + +.. code-block:: arduino + + rmt_data_t symbols[] = { + {8, 1, 4, 0}, // High for 8 ticks, Low for 4 ticks (bit '1') + {4, 1, 8, 0} // High for 4 ticks, Low for 8 ticks (bit '0') + }; + + rmtWrite(pin, symbols, 2, RMT_WAIT_FOR_EVER); + +rmtWriteAsync +************* + +Sends RMT data in non-blocking (asynchronous) mode. The function returns immediately after starting the transmission. + +.. code-block:: arduino + + bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); + +* ``pin`` - GPIO pin number configured for RMT TX mode +* ``data`` - Pointer to array of ``rmt_data_t`` symbols to transmit +* ``num_rmt_symbols`` - Number of RMT symbols to transmit + +**Non-blocking mode:** Returns immediately after execution. Use ``rmtTransmitCompleted()`` to check if transmission is finished. + +**Note:** If ``rmtWriteAsync()`` is called while a previous transmission is still in progress, it will return ``false`` immediately to indicate failure; it does not wait for the previous transmission to finish. + +This function returns ``true`` on execution success, ``false`` otherwise. + +rmtWriteLooping +**************** + +Sends RMT data in infinite looping mode. The data will be transmitted continuously until stopped. + +.. code-block:: arduino + + bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols); + +* ``pin`` - GPIO pin number configured for RMT TX mode +* ``data`` - Pointer to array of ``rmt_data_t`` symbols to transmit +* ``num_rmt_symbols`` - Number of RMT symbols to transmit + +**Looping mode:** The data is transmitted continuously in a loop. To stop looping, call ``rmtWrite()`` or ``rmtWriteAsync()`` with new data, or call ``rmtWriteLooping()`` with ``NULL`` data or zero size. + +**Note:** Looping mode needs a zero-ending data symbol ``{0, 0, 0, 0}`` to mark the end of data. + +This function returns ``true`` on execution success, ``false`` otherwise. + +rmtWriteRepeated +**************** + +Sends RMT data a fixed number of times (repeated transmission). + +.. code-block:: arduino + + bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count); + +* ``pin`` - GPIO pin number configured for RMT TX mode +* ``data`` - Pointer to array of ``rmt_data_t`` symbols to transmit +* ``num_rmt_symbols`` - Number of RMT symbols to transmit +* ``loop_count`` - Number of times to repeat the transmission (must be at least 1) + +**Note:** +* ``loop_count == 0`` is invalid (no transmission) +* ``loop_count == 1`` transmits once (no looping) +* ``loop_count > 1`` transmits the data repeatedly + +**Note:** Loop count feature is only supported on certain SoCs. On unsupported SoCs, this function will return ``false``. + +This function returns ``true`` on execution success, ``false`` otherwise. + +rmtTransmitCompleted +******************** + +Checks if the RMT transmission is completed and the channel is ready for transmitting new data. + +.. code-block:: arduino + + bool rmtTransmitCompleted(int pin); + +* ``pin`` - GPIO pin number configured for RMT TX mode + +This function returns ``true`` when all data has been sent and the channel is ready for a new transmission, ``false`` otherwise. + +**Note:** +* If ``rmtWrite()`` times out or ``rmtWriteAsync()`` is called, this function will return ``false`` until all data is sent out. +* ``rmtTransmitCompleted()`` will always return ``true`` when ``rmtWriteLooping()`` is active, because it has no effect in such case. + +rmtRead +******* + +Initiates blocking receive operation. Reads RMT data and stores it in the provided buffer. + +.. code-block:: arduino + + bool rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, uint32_t timeout_ms); + +* ``pin`` - GPIO pin number configured for RMT RX mode +* ``data`` - Pointer to buffer where received RMT symbols will be stored +* ``num_rmt_symbols`` - Pointer to variable containing maximum number of symbols to read. + + On return, this variable will contain the actual number of symbols read. + +* ``timeout_ms`` - Timeout in milliseconds. Use ``RMT_WAIT_FOR_EVER`` for indefinite wait + +**Blocking mode:** The function waits until data is received or timeout occurs. + +If the reading operation times out, ``num_rmt_symbols`` won't change and ``rmtReceiveCompleted()`` can be used later to check if data is available. + +This function returns ``true`` when data is successfully read, ``false`` on error or timeout. + +rmtReadAsync +************ + +Initiates non-blocking (asynchronous) receive operation. Returns immediately after starting the receive process. + +.. code-block:: arduino + + bool rmtReadAsync(int pin, rmt_data_t *data, size_t *num_rmt_symbols); + +* ``pin`` - GPIO pin number configured for RMT RX mode +* ``data`` - Pointer to buffer where received RMT symbols will be stored +* ``num_rmt_symbols`` - Pointer to variable containing maximum number of symbols to read. + + On completion, this variable will be updated with the actual number of symbols read. + +**Non-blocking mode:** Returns immediately after execution. Use ``rmtReceiveCompleted()`` to check if data is available. + +This function returns ``true`` on execution success, ``false`` otherwise. + +rmtReceiveCompleted +******************* + +Checks if RMT data reception is completed and new data is available for processing. + +.. code-block:: arduino + + bool rmtReceiveCompleted(int pin); + +* ``pin`` - GPIO pin number configured for RMT RX mode + +This function returns ``true`` when data has been received and is available in the buffer, ``false`` otherwise. + +**Note:** The data reception information is reset when a new ``rmtRead()`` or ``rmtReadAsync()`` function is called. + +rmtSetCarrier +************* + +Sets carrier frequency modulation/demodulation for RMT TX or RX channel. + +.. code-block:: arduino + + bool rmtSetCarrier(int pin, bool carrier_en, bool carrier_level, uint32_t frequency_Hz, float duty_percent); + +* ``pin`` - GPIO pin number configured for RMT +* ``carrier_en`` - Enable/disable carrier modulation (TX) or demodulation (RX) +* ``carrier_level`` - Carrier polarity level: + + * ``true`` - Positive polarity (active high) + * ``false`` - Negative polarity (active low) + +* ``frequency_Hz`` - Carrier frequency in Hz (e.g., 38000 for 38 kHz IR carrier) +* ``duty_percent`` - Duty cycle as a float from 0.0 to 1.0 (e.g., 0.33 for 33% duty cycle, 0.5 for 50% square wave) + +**Note:** Parameters changed in Arduino Core 3: low and high (ticks) are now expressed in Carrier Frequency in Hz and duty cycle in percentage (float 0.0 to 1.0). + +**Example:** 38.5 kHz carrier with 33% duty cycle: ``rmtSetCarrier(pin, true, true, 38500, 0.33)`` + +This function returns ``true`` if carrier is set successfully, ``false`` otherwise. + +rmtSetRxMinThreshold +******************** + +Sets the minimum pulse width filter threshold for RX channel. Pulses smaller than this threshold will be ignored as noise. + +.. code-block:: arduino + + bool rmtSetRxMinThreshold(int pin, uint8_t filter_pulse_ticks); + +* ``pin`` - GPIO pin number configured for RMT RX mode +* ``filter_pulse_ticks`` - Minimum pulse width in RMT ticks. Pulses (high or low) smaller than this will be filtered out. + + Set to ``0`` to disable the filter. + +**Note:** The filter threshold is specified in RMT ticks, which depends on the RMT frequency set during ``rmtInit()``. + +This function returns ``true`` if filter threshold is set successfully, ``false`` otherwise. + +rmtSetRxMaxThreshold +******************** + +Sets the maximum idle threshold for RX channel. When no edge is detected for longer than this threshold, the receiving process is finished. + +.. code-block:: arduino + + bool rmtSetRxMaxThreshold(int pin, uint16_t idle_thres_ticks); + +* ``pin`` - GPIO pin number configured for RMT RX mode +* ``idle_thres_ticks`` - Maximum idle time in RMT ticks. When no edge is detected for longer than this time, reception ends. + + This threshold also defines how many low/high bits are read at the end of the received data. + +**Note:** The idle threshold is specified in RMT ticks, which depends on the RMT frequency set during ``rmtInit()``. + +This function returns ``true`` if idle threshold is set successfully, ``false`` otherwise. + +RMT Data Structure +------------------ + +rmt_data_t +********** + +RMT data structure representing a single RMT symbol (two consecutive pulses). + +.. code-block:: arduino + + typedef union { + struct { + uint32_t duration0 : 15; // Duration of first pulse in RMT ticks + uint32_t level0 : 1; // Level of first pulse (0 = LOW, 1 = HIGH) + uint32_t duration1 : 15; // Duration of second pulse in RMT ticks + uint32_t level1 : 1; // Level of second pulse (0 = LOW, 1 = HIGH) + }; + uint32_t val; // Access as 32-bit value + } rmt_data_t; + +Each RMT symbol contains two pulses: +* **First pulse:** ``level0`` for ``duration0`` ticks +* **Second pulse:** ``level1`` for ``duration1`` ticks + +**Example:** + +.. code-block:: arduino + + // Create a symbol: HIGH for 8 ticks, then LOW for 4 ticks + rmt_data_t symbol = { + .duration0 = 8, + .level0 = 1, + .duration1 = 4, + .level1 = 0 + }; + + // Or using struct initialization + rmt_data_t symbol2 = {8, 1, 4, 0}; + +Helper Macros +------------- + +RMT_SYMBOLS_OF +************** + +Helper macro to calculate the number of RMT symbols in an array. + +.. code-block:: arduino + + #define RMT_SYMBOLS_OF(x) (sizeof(x) / sizeof(rmt_data_t)) + +**Example:** + +.. code-block:: arduino + + rmt_data_t data[] = { + {8, 1, 4, 0}, + {4, 1, 8, 0} + }; + + size_t num_symbols = RMT_SYMBOLS_OF(data); // Returns 2 + +RMT_WAIT_FOR_EVER ***************** +Constant for indefinite timeout in blocking operations. + +.. code-block:: arduino + + #define RMT_WAIT_FOR_EVER ((uint32_t)portMAX_DELAY) + +Use this constant as the ``timeout_ms`` parameter in ``rmtWrite()`` or ``rmtRead()`` to wait indefinitely. + +**Example:** + +.. code-block:: arduino + + rmtWrite(pin, data, num_symbols, RMT_WAIT_FOR_EVER); + +RMT_SYMBOLS_PER_CHANNEL_BLOCK +****************************** + +Constant defining the number of RMT symbols per memory block. + +* ESP32/ESP32-S2: 64 symbols per block +* ESP32-S3/ESP32-C3/ESP32-C5/ESP32-C6/ESP32-H2/ESP32-P4: 48 symbols per block + +**Example:** + +.. code-block:: arduino + + // Allocate buffer for 1 memory block + rmt_data_t buffer[RMT_SYMBOLS_PER_CHANNEL_BLOCK]; + +Example Applications +******************** + +RMT Write RGB LED (WS2812): +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. literalinclude:: ../../../libraries/ESP32/examples/RMT/RMTWrite_RGB_LED/RMTWrite_RGB_LED.ino :language: arduino +RMT LED Blink: +^^^^^^^^^^^^^^ + +.. literalinclude:: ../../../libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino + :language: arduino + +RMT Read XJT Protocol: +^^^^^^^^^^^^^^^^^^^^^^^ + +.. literalinclude:: ../../../libraries/ESP32/examples/RMT/RMTReadXJT/RMTReadXJT.ino + :language: arduino Complete list of `RMT examples `_. diff --git a/docs/en/api/serial.rst b/docs/en/api/serial.rst new file mode 100644 index 00000000000..86ddd26a891 --- /dev/null +++ b/docs/en/api/serial.rst @@ -0,0 +1,671 @@ +############# +Serial (UART) +############# + +About +----- +The Serial (UART - Universal Asynchronous Receiver-Transmitter) peripheral provides asynchronous serial communication, +allowing the ESP32 to communicate with other devices such as computers, sensors, displays, and other microcontrollers. + +UART is a simple, two-wire communication protocol that uses a TX (transmit) and RX (receive) line for full-duplex communication. +The ESP32 Arduino implementation provides a HardwareSerial class that is compatible with the standard Arduino Serial API, +with additional features for advanced use cases. + +**Key Features:** + +* **Full-duplex communication**: Simultaneous transmission and reception +* **Configurable baud rates**: From 300 to 5,000,000+ baud +* **Multiple data formats**: Configurable data bits, parity, and stop bits +* **Hardware flow control**: Support for RTS/CTS signals +* **RS485 support**: Half-duplex RS485 communication mode +* **Low-power UART**: Some SoCs support LP (Low-Power) UART for ultra-low power applications +* **Baud rate detection**: Automatic baud rate detection (ESP32, ESP32-S2 only) +* **Event callbacks**: Receive and error event callbacks +* **Configurable buffers**: Adjustable RX and TX buffer sizes + +.. note:: + In case that both pins, RX and TX are detached from UART, the driver will be stopped. + Detaching may occur when, for instance, starting another peripheral using RX and TX pins, such as Wire.begin(RX0, TX0). + +UART Availability +----------------- + +The number of UART peripherals available varies by ESP32 SoC: + +========= ======== ======== +ESP32 SoC HP UARTs LP UARTs +========= ======== ======== +ESP32 3 0 +ESP32-S2 2 0 +ESP32-S3 3 0 +ESP32-C3 2 0 +ESP32-C5 2 1 +ESP32-C6 2 1 +ESP32-H2 2 0 +ESP32-P4 5 1 +========= ======== ======== + +**Note:** +* HP (High-Performance) UARTs are the standard UART peripherals +* LP (Low-Power) UARTs are available on some SoCs for ultra-low power applications +* UART0 is typically used for programming and debug output (Serial Monitor) +* Additional UARTs (Serial1, Serial2, etc.) are available for general-purpose communication, including LP UARTs when available. The ESP32 Arduino Core automatically creates HardwareSerial objects for all available UARTs: + + * ``Serial0`` (or ``Serial``) - UART0 (HP UART, typically used for programming and debug output) + * ``Serial1``, ``Serial2``, etc. - Additional HP UARTs (numbered sequentially) + * Additional Serial objects - LP UARTs, when available (numbered after HP UARTs) + + **Example:** The ESP32-C6 has 2 HP UARTs and 1 LP UART. The Arduino Core creates ``Serial0`` and ``Serial1`` (HP UARTs) plus ``Serial2`` (LP UART) HardwareSerial objects. + + **Important:** LP UARTs can be used as regular UART ports, but they have fixed GPIO pins for RX, TX, CTS, and RTS. It is not possible to change the pins for LP UARTs using ``setPins()``. + +Arduino-ESP32 Serial API +------------------------ + +begin +***** + +Initializes the Serial port with the specified baud rate and configuration. + +.. code-block:: arduino + + void begin(unsigned long baud, uint32_t config = SERIAL_8N1, int8_t rxPin = -1, int8_t txPin = -1, bool invert = false, unsigned long timeout_ms = 20000UL, uint8_t rxfifo_full_thrhd = 120); + +* ``baud`` - Baud rate (bits per second). Common values: 9600, 115200, 230400, etc. + + **Special value:** ``0`` enables baud rate detection (ESP32, ESP32-S2 only). The function will attempt to detect the baud rate for up to ``timeout_ms`` milliseconds. See the :ref:`Baud Rate Detection Example ` for usage details. +* ``config`` - Serial configuration (data bits, parity, stop bits): + + * ``SERIAL_8N1`` - 8 data bits, no parity, 1 stop bit (default) + * ``SERIAL_8N2`` - 8 data bits, no parity, 2 stop bits + * ``SERIAL_8E1`` - 8 data bits, even parity, 1 stop bit + * ``SERIAL_8E2`` - 8 data bits, even parity, 2 stop bits + * ``SERIAL_8O1`` - 8 data bits, odd parity, 1 stop bit + * ``SERIAL_8O2`` - 8 data bits, odd parity, 2 stop bits + * ``SERIAL_7N1``, ``SERIAL_7N2``, ``SERIAL_7E1``, ``SERIAL_7E2``, ``SERIAL_7O1``, ``SERIAL_7O2`` - 7 data bits variants + * ``SERIAL_6N1``, ``SERIAL_6N2``, ``SERIAL_6E1``, ``SERIAL_6E2``, ``SERIAL_6O1``, ``SERIAL_6O2`` - 6 data bits variants + * ``SERIAL_5N1``, ``SERIAL_5N2``, ``SERIAL_5E1``, ``SERIAL_5E2``, ``SERIAL_5O1``, ``SERIAL_5O2`` - 5 data bits variants + +* ``rxPin`` - RX pin number. Use ``-1`` to keep the default pin or current pin assignment. + +* ``txPin`` - TX pin number. Use ``-1`` to keep the default pin or current pin assignment. + +* ``invert`` - If ``true``, inverts the RX and TX signal polarity. + +* ``timeout_ms`` - Timeout in milliseconds for baud rate detection (when ``baud = 0``). Default: 20000 ms (20 seconds). + +* ``rxfifo_full_thrhd`` - RX FIFO full threshold (1-127 bytes). When the FIFO reaches this threshold, data is copied to the RX buffer. Default: 120 bytes. + +**Example:** + +.. code-block:: arduino + + // Basic initialization with default pins + Serial.begin(115200); + + // Initialize with custom pins + Serial1.begin(9600, SERIAL_8N1, 4, 5); + + // Initialize with baud rate detection (ESP32, ESP32-S2 only) + Serial.begin(0, SERIAL_8N1, -1, -1, false, 20000); + +end +*** + +Stops the Serial port and releases all resources. + +.. code-block:: arduino + + void end(void); + +This function disables the UART peripheral and frees all associated resources. + +available +********* + +Returns the number of bytes available for reading from the Serial port. + +.. code-block:: arduino + + int available(void); + +**Returns:** The number of bytes available in the RX buffer, or ``0`` if no data is available. + +**Example:** + +.. code-block:: arduino + + if (Serial.available() > 0) { + char data = Serial.read(); + } + +availableForWrite +***************** + +Returns the number of bytes that can be written to the Serial port without blocking. + +.. code-block:: arduino + + int availableForWrite(void); + +**Returns:** The number of bytes that can be written to the TX buffer without blocking. + +read +**** + +Reads a single byte from the Serial port. + +.. code-block:: arduino + + int read(void); + +**Returns:** The byte read (0-255), or ``-1`` if no data is available. + +**Example:** + +.. code-block:: arduino + + int data = Serial.read(); + if (data != -1) { + Serial.printf("Received: %c\n", data); + } + +read (buffer) +************* + +Reads multiple bytes from the Serial port into a buffer. + +.. code-block:: arduino + + size_t read(uint8_t *buffer, size_t size); + size_t read(char *buffer, size_t size); + +* ``buffer`` - Pointer to the buffer where data will be stored +* ``size`` - Maximum number of bytes to read + +**Returns:** The number of bytes actually read. + +**Example:** + +.. code-block:: arduino + + uint8_t buffer[64]; + size_t bytesRead = Serial.read(buffer, sizeof(buffer)); + Serial.printf("Read %d bytes\n", bytesRead); + +readBytes +********* + +Reads multiple bytes from the Serial port, blocking until the specified number of bytes is received or timeout occurs. + +.. code-block:: arduino + + size_t readBytes(uint8_t *buffer, size_t length); + size_t readBytes(char *buffer, size_t length); + +* ``buffer`` - Pointer to the buffer where data will be stored +* ``length`` - Number of bytes to read + +**Returns:** The number of bytes actually read (may be less than ``length`` if timeout occurs). + +**Note:** This function overrides ``Stream::readBytes()`` for better performance using ESP-IDF functions. + +write +***** + +Writes data to the Serial port. + +.. code-block:: arduino + + size_t write(uint8_t); + size_t write(const uint8_t *buffer, size_t size); + size_t write(const char *buffer, size_t size); + size_t write(const char *s); + size_t write(unsigned long n); + size_t write(long n); + size_t write(unsigned int n); + size_t write(int n); + +* Single byte: ``write(uint8_t)`` - Writes a single byte +* Buffer: ``write(buffer, size)`` - Writes multiple bytes from a buffer +* String: ``write(const char *s)`` - Writes a null-terminated string +* Number: ``write(n)`` - Writes a number as a single byte + +**Returns:** The number of bytes written. + +**Example:** + +.. code-block:: arduino + + Serial.write('A'); + Serial.write("Hello"); + Serial.write(buffer, 10); + Serial.write(65); // Writes byte value 65 + +peek +**** + +Returns the next byte in the RX buffer without removing it. + +.. code-block:: arduino + + int peek(void); + +**Returns:** The next byte (0-255), or ``-1`` if no data is available. + +**Note:** Unlike ``read()``, ``peek()`` does not remove the byte from the buffer. + +flush +***** + +Waits for all data in the TX buffer to be transmitted. + +.. code-block:: arduino + + void flush(void); + void flush(bool txOnly); + +* ``txOnly`` - If ``true``, only flushes the TX buffer. If ``false`` (default), also clears the RX buffer. + +**Note:** This function blocks until all data in the TX buffer has been sent. + +baudRate +******** + +Returns the current baud rate of the Serial port. + +.. code-block:: arduino + + uint32_t baudRate(void); + +**Returns:** The configured baud rate in bits per second. + +**Note:** When using baud rate detection (``begin(0)``), this function returns the detected baud rate, which may be slightly rounded (e.g., 115200 may return 115201). + +updateBaudRate +************** + +Updates the baud rate of an already initialized Serial port. + +.. code-block:: arduino + + void updateBaudRate(unsigned long baud); + +* ``baud`` - New baud rate + +**Note:** This function can be called after ``begin()`` to change the baud rate without reinitializing the port. + +setPins +******* + +Sets or changes the RX, TX, CTS, and RTS pins for the Serial port. + +.. code-block:: arduino + + bool setPins(int8_t rxPin, int8_t txPin, int8_t ctsPin = -1, int8_t rtsPin = -1); + +* ``rxPin`` - RX pin number. Use ``-1`` to keep current pin. +* ``txPin`` - TX pin number. Use ``-1`` to keep current pin. +* ``ctsPin`` - CTS (Clear To Send) pin for hardware flow control. Use ``-1`` to keep current pin or disable. +* ``rtsPin`` - RTS (Request To Send) pin for hardware flow control. Use ``-1`` to keep current pin or disable. + +**Returns:** ``true`` if pins are set successfully, ``false`` otherwise. + +**Note:** This function can be called before or after ``begin()``. When pins are changed, the previous pins are automatically detached. + +setRxBufferSize +*************** + +Sets the size of the RX buffer. + +.. code-block:: arduino + + size_t setRxBufferSize(size_t new_size); + +* ``new_size`` - New RX buffer size in bytes + +**Returns:** The actual buffer size set, or ``0`` on error. + +**Note:** This function must be called **before** ``begin()`` to take effect. Default RX buffer size is 256 bytes. + +setTxBufferSize +*************** + +Sets the size of the TX buffer. + +.. code-block:: arduino + + size_t setTxBufferSize(size_t new_size); + +* ``new_size`` - New TX buffer size in bytes + +**Returns:** The actual buffer size set, or ``0`` on error. + +**Note:** This function must be called **before** ``begin()`` to take effect. Default TX buffer size is 0 (no buffering). + +setRxTimeout +************ + +Sets the RX timeout threshold in UART symbol periods. + +.. code-block:: arduino + + bool setRxTimeout(uint8_t symbols_timeout); + +* ``symbols_timeout`` - Timeout threshold in UART symbol periods. Setting ``0`` disables timeout-based callbacks. + + The timeout is calculated based on the current baud rate and serial configuration. For example: + + * For ``SERIAL_8N1`` (10 bits per symbol), a timeout of 3 symbols at 9600 baud = 3 / (9600 / 10) = 3.125 ms + * Maximum timeout is calculated automatically by ESP-IDF based on the serial configuration + +**Returns:** ``true`` if timeout is set successfully, ``false`` otherwise. + +**Note:** +* When RX timeout occurs, the ``onReceive()`` callback is triggered +* For ESP32 and ESP32-S2, when using REF_TICK clock source (baud rates ≤ 250000), RX timeout is limited to 1 symbol +* To use higher RX timeout values on ESP32/ESP32-S2, set the clock source to APB using ``setClockSource(UART_CLK_SRC_APB)`` before ``begin()`` + +setRxFIFOFull +************* + +Sets the RX FIFO full threshold that triggers data transfer from FIFO to RX buffer. + +.. code-block:: arduino + + bool setRxFIFOFull(uint8_t fifoBytes); + +* ``fifoBytes`` - Number of bytes (1-127) that will trigger the FIFO full interrupt + + When the UART FIFO reaches this threshold, data is copied to the RX buffer and the ``onReceive()`` callback is triggered. + +**Returns:** ``true`` if threshold is set successfully, ``false`` otherwise. + +**Note:** +* Lower values (e.g., 1) provide byte-by-byte reception but consume more CPU time +* Higher values (e.g., 120) provide better performance but introduce latency +* Default value depends on baud rate: 1 byte for ≤ 115200 baud, 120 bytes for > 115200 baud + +onReceive +********* + +Sets a callback function that is called when data is received. + +.. code-block:: arduino + + void onReceive(OnReceiveCb function, bool onlyOnTimeout = false); + +* ``function`` - Callback function to call when data is received. Use ``NULL`` to disable the callback. +* ``onlyOnTimeout`` - If ``true``, callback is only called on RX timeout. If ``false`` (default), callback is called on both FIFO full and RX timeout events. + +**Callback Signature:** + +.. code-block:: arduino + + typedef std::function OnReceiveCb; + +**Note:** +* When ``onlyOnTimeout = false``, the callback is triggered when FIFO reaches the threshold (set by ``setRxFIFOFull()``) or on RX timeout +* When ``onlyOnTimeout = true``, the callback is only triggered on RX timeout, ensuring all data in a stream is available at once +* Using ``onlyOnTimeout = true`` may cause RX overflow if the RX buffer size is too small for the incoming data stream +* The callback is executed in a separate task, allowing non-blocking data processing + +**Example:** + +.. code-block:: arduino + + void onReceiveCallback() { + while (Serial1.available()) { + char c = Serial1.read(); + Serial.print(c); + } + } + + void setup() { + Serial1.begin(115200); + Serial1.onReceive(onReceiveCallback); + } + +onReceiveError +************** + +Sets a callback function that is called when a UART error occurs. + +.. code-block:: arduino + + void onReceiveError(OnReceiveErrorCb function); + +* ``function`` - Callback function to call when an error occurs. Use ``NULL`` to disable the callback. + +**Callback Signature:** + +.. code-block:: arduino + + typedef std::function OnReceiveErrorCb; + +**Error Types:** + +* ``UART_NO_ERROR`` - No error +* ``UART_BREAK_ERROR`` - Break condition detected +* ``UART_BUFFER_FULL_ERROR`` - RX buffer is full +* ``UART_FIFO_OVF_ERROR`` - UART FIFO overflow +* ``UART_FRAME_ERROR`` - Frame error (invalid stop bit) +* ``UART_PARITY_ERROR`` - Parity error + +**Example:** + +.. code-block:: arduino + + void onErrorCallback(hardwareSerial_error_t error) { + Serial.printf("UART Error: %d\n", error); + } + + void setup() { + Serial1.begin(115200); + Serial1.onReceiveError(onErrorCallback); + } + +eventQueueReset +*************** + +Clears all events in the event queue (events that trigger ``onReceive()`` and ``onReceiveError()``). + +.. code-block:: arduino + + void eventQueueReset(void); + +This function can be useful in some use cases where you want to clear pending events. + +setHwFlowCtrlMode +***************** + +Enables or disables hardware flow control using RTS and/or CTS pins. + +.. code-block:: arduino + + bool setHwFlowCtrlMode(SerialHwFlowCtrl mode = UART_HW_FLOWCTRL_CTS_RTS, uint8_t threshold = 64); + +* ``mode`` - Hardware flow control mode: + + * ``UART_HW_FLOWCTRL_DISABLE`` (0x0) - Disable hardware flow control + * ``UART_HW_FLOWCTRL_RTS`` (0x1) - Enable RX hardware flow control (RTS) + * ``UART_HW_FLOWCTRL_CTS`` (0x2) - Enable TX hardware flow control (CTS) + * ``UART_HW_FLOWCTRL_CTS_RTS`` (0x3) - Enable full hardware flow control (default) + +* ``threshold`` - Flow control threshold (default: 64, which is half of the FIFO length) + +**Returns:** ``true`` if flow control mode is set successfully, ``false`` otherwise. + +**Note:** CTS and RTS pins must be set using ``setPins()`` before enabling hardware flow control. + +setMode +******* + +Sets the UART operating mode. + +.. code-block:: arduino + + bool setMode(SerialMode mode); + +* ``mode`` - UART mode: + + * ``UART_MODE_UART`` (0x00) - Regular UART mode (default) + * ``UART_MODE_RS485_HALF_DUPLEX`` (0x01) - Half-duplex RS485 mode (RTS pin controls transceiver) + * ``UART_MODE_IRDA`` (0x02) - IRDA UART mode + * ``UART_MODE_RS485_COLLISION_DETECT`` (0x03) - RS485 collision detection mode (for testing) + * ``UART_MODE_RS485_APP_CTRL`` (0x04) - Application-controlled RS485 mode (for testing) + +**Returns:** ``true`` if mode is set successfully, ``false`` otherwise. + +**Note:** For RS485 half-duplex mode, the RTS pin must be configured using ``setPins()`` to control the transceiver. + +setClockSource +************** + +Sets the UART clock source. Must be called **before** ``begin()`` to take effect. + +.. code-block:: arduino + + bool setClockSource(SerialClkSrc clkSrc); + +* ``clkSrc`` - Clock source: + + * ``UART_CLK_SRC_DEFAULT`` - Default clock source (varies by SoC) + * ``UART_CLK_SRC_APB`` - APB clock (ESP32, ESP32-S2, ESP32-C3, ESP32-S3) + * ``UART_CLK_SRC_PLL`` - PLL clock (ESP32-C2, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-P4) + * ``UART_CLK_SRC_XTAL`` - XTAL clock (ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3, ESP32-P4) + * ``UART_CLK_SRC_RTC`` - RTC clock (ESP32-C2, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-C61, ESP32-H2, ESP32-S3, ESP32-P4) + * ``UART_CLK_SRC_REF_TICK`` - REF_TICK clock (ESP32, ESP32-S2) + +**Note:** +* Clock source availability varies by SoC. +* PLL frequency varies by SoC: ESP32-C2 (40 MHz), ESP32-H2 (48 MHz), ESP32-C5/C6/C61/P4 (80 MHz). +* ESP32-C5, ESP32-C6, ESP32-C61, and ESP32-P4 have LP UART that uses only RTC_FAST or XTAL/2 as clock source. +* For ESP32 and ESP32-S2, REF_TICK is used by default for baud rates ≤ 250000 to avoid baud rate changes when CPU frequency changes, but this limits RX timeout to 1 symbol. + +**Returns:** ``true`` if clock source is set successfully, ``false`` otherwise. + +setRxInvert +*********** + +Enables or disables RX signal inversion. + +.. code-block:: arduino + + bool setRxInvert(bool invert); + +* ``invert`` - If ``true``, inverts the RX signal polarity + +**Returns:** ``true`` if inversion is set successfully, ``false`` otherwise. + +setTxInvert +*********** + +Enables or disables TX signal inversion. + +.. code-block:: arduino + + bool setTxInvert(bool invert); + +* ``invert`` - If ``true``, inverts the TX signal polarity + +**Returns:** ``true`` if inversion is set successfully, ``false`` otherwise. + +setCtsInvert +************ + +Enables or disables CTS signal inversion. + +.. code-block:: arduino + + bool setCtsInvert(bool invert); + +* ``invert`` - If ``true``, inverts the CTS signal polarity + +**Returns:** ``true`` if inversion is set successfully, ``false`` otherwise. + +setRtsInvert +************ + +Enables or disables RTS signal inversion. + +.. code-block:: arduino + + bool setRtsInvert(bool invert); + +* ``invert`` - If ``true``, inverts the RTS signal polarity + +**Returns:** ``true`` if inversion is set successfully, ``false`` otherwise. + +setDebugOutput +************** + +Enables or disables debug output on this Serial port. + +.. code-block:: arduino + + void setDebugOutput(bool enable); + +* ``enable`` - If ``true``, enables debug output (ESP-IDF log messages will be sent to this Serial port) + +**Note:** By default, debug output is sent to UART0 (Serial0). + +operator bool +************* + +Returns whether the Serial port is initialized and ready. + +.. code-block:: arduino + + operator bool() const; + +**Returns:** ``true`` if the Serial port is initialized, ``false`` otherwise. + +**Example:** + +.. code-block:: arduino + + Serial1.begin(115200); + while (!Serial1) { + delay(10); // Wait for Serial1 to be ready + } + +Serial Configuration Constants +------------------------------ + +The following constants are used for serial configuration in the ``begin()`` function: + +Data Bits, Parity, Stop Bits +**************************** + +* ``SERIAL_5N1``, ``SERIAL_5N2``, ``SERIAL_5E1``, ``SERIAL_5E2``, ``SERIAL_5O1``, ``SERIAL_5O2`` - 5 data bits +* ``SERIAL_6N1``, ``SERIAL_6N2``, ``SERIAL_6E1``, ``SERIAL_6E2``, ``SERIAL_6O1``, ``SERIAL_6O2`` - 6 data bits +* ``SERIAL_7N1``, ``SERIAL_7N2``, ``SERIAL_7E1``, ``SERIAL_7E2``, ``SERIAL_7O1``, ``SERIAL_7O2`` - 7 data bits +* ``SERIAL_8N1``, ``SERIAL_8N2``, ``SERIAL_8E1``, ``SERIAL_8E2``, ``SERIAL_8O1``, ``SERIAL_8O2`` - 8 data bits + +Where: +* First number = data bits (5, 6, 7, or 8) +* Letter = parity: N (None), E (Even), O (Odd) +* Last number = stop bits (1 or 2) + +Example Applications +******************** + +.. _baud-rate-detection-example: + +Baud Rate Detection Example: + +.. literalinclude:: ../../../libraries/ESP32/examples/Serial/BaudRateDetect_Demo/BaudRateDetect_Demo.ino + :language: arduino + +OnReceive Callback Example: + +.. literalinclude:: ../../../libraries/ESP32/examples/Serial/OnReceive_Demo/OnReceive_Demo.ino + :language: arduino + +RS485 Communication Example: + +.. literalinclude:: ../../../libraries/ESP32/examples/Serial/RS485_Echo_Demo/RS485_Echo_Demo.ino + :language: arduino + +Complete list of `Serial examples `_. diff --git a/docs/en/api/spi.rst b/docs/en/api/spi.rst index c82b0de5ccb..27c78be9569 100644 --- a/docs/en/api/spi.rst +++ b/docs/en/api/spi.rst @@ -10,7 +10,7 @@ For some APIs, the reference to be used is the same as the Arduino Core. Arduino API Reference --------------------- -`SPI Reference `_ +`SPI Reference `_ `SPI Description `_ diff --git a/docs/en/contributing.rst b/docs/en/contributing.rst index 7a7b99894eb..c9f83530d36 100644 --- a/docs/en/contributing.rst +++ b/docs/en/contributing.rst @@ -113,21 +113,18 @@ Testing ******* Be sure you have tested the example in all the supported targets. If the example some specific hardware requirements, -edit/add the ``ci.json`` in the same folder as the sketch to specify the regular expression for the +edit/add the ``ci.yml`` in the same folder as the sketch to specify the regular expression for the required configurations from ``sdkconfig``. This will ensure that the CI system will run the test only on the targets that have the required configurations. You can check the available configurations in the ``sdkconfig`` file in the ``tools/esp32-arduino-libs/`` folder. -Here is an example of the ``ci.json`` file where the example requires Wi-Fi to work properly: +Here is an example of the ``ci.yml`` file where the example requires Wi-Fi to work properly: -.. code-block:: json +.. code-block:: yaml - { - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] - } + requires: + - CONFIG_SOC_WIFI_SUPPORTED=y .. note:: @@ -138,21 +135,17 @@ Here is an example of the ``ci.json`` file where the example requires Wi-Fi to w That means that the configuration must be at the beginning of the line in the ``sdkconfig`` file. Sometimes, the example might not be supported by some target, even if the target has the required configurations -(like resources limitations or requiring a specific SoC). To avoid compilation errors, you can add the target to the ``ci.json`` +(like resources limitations or requiring a specific SoC). To avoid compilation errors, you can add the target to the ``ci.yml`` file so the CI system will force to skip the test on that target. -Here is an example of the ``ci.json`` file where the example is requires Wi-Fi to work properly but is also not supported by the ESP32-S2 target: +Here is an example of the ``ci.yml`` file where the example is requires Wi-Fi to work properly but is also not supported by the ESP32-S2 target: -.. code-block:: json +.. code-block:: yaml - { - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ], - "targets": { - "esp32s2": false - } - } + requires: + - CONFIG_SOC_WIFI_SUPPORTED=y + targets: + esp32s2: false You also need to add this information in the ``README.md`` file, on the **Supported Targets**, and in the example code as an inline comment. For example, in the sketch: @@ -183,29 +176,23 @@ Currently, the default FQBNs are: * ``espressif:esp32:esp32h2`` * ``espressif:esp32:esp32p4:USBMode=default`` -There are two ways to alter the FQBNs used to compile the sketches: by using the ``fqbn`` or ``fqbn_append`` fields in the ``ci.json`` file. +There are two ways to alter the FQBNs used to compile the sketches: by using the ``fqbn`` or ``fqbn_append`` fields in the ``ci.yml`` file. If you just want to append a string to the default FQBNs, you can use the ``fqbn_append`` field. For example, to add the ``DebugLevel=debug`` to the FQBNs, you would use: -.. code-block:: json +.. code-block:: yaml - { - "fqbn_append": "DebugLevel=debug" - } + fqbn_append: DebugLevel=debug If you want to override the default FQBNs, you can use the ``fqbn`` field. It is a dictionary where the key is the target name and the value is a list of FQBNs. The FQBNs in the list will be used in sequence to compile the sketch. For example, to compile a sketch for ESP32-S2 with and without PSRAM enabled, you would use: -.. code-block:: json +.. code-block:: yaml - { - "fqbn": { - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=enabled,FlashMode=dio", - "espressif:esp32:esp32s2:PSRAM=disabled,FlashMode=dio" - ] - } - } + fqbn: + esp32s2: + - espressif:esp32:esp32s2:PSRAM=enabled,FlashMode=dio + - espressif:esp32:esp32s2:PSRAM=disabled,FlashMode=dio .. note:: @@ -301,6 +288,13 @@ The tests are divided into two categories inside the ``tests`` folder: to the core and libraries have any big impact on the performance. These tests usually run for a longer time than the validation tests and include common benchmark tools like `CoreMark `_. +.. note:: + + Keep in mind that to run the CI scripts locally, you need to have the Go-based ``yq`` from `mikefarah/yq `_ + installed and not the Python-based ``yq`` from PyPI or `kislyuk/yq `_. + The syntax is slightly different and will not work with the CI scripts. + You can install the Go-based ``yq`` through ``brew``, ``chocolatey`` or downloading the binary directly from the `releases page `_. + To run the runtime tests locally, first install the required dependencies by running: .. code-block:: bash @@ -365,7 +359,7 @@ After the test is finished, you can check the output in the terminal and the gen Additionally, for performance tests, you can check the generated JSON file in the same folder. You can also run the tests in `Wokwi `_ or `Espressif's QEMU `_ -by using the ``-W `` and ``-Q`` flags respectively. You will need to have the Wokwi and/or QEMU installed in your system +by using the ``-W`` and ``-Q`` flags respectively. You will need to have the Wokwi and/or QEMU installed in your system and set the ``WOKWI_CLI_TOKEN`` and/or ``QEMU_PATH`` environment variables. The ``WOKWI_CLI_TOKEN`` is the CI token that can be obtained from the `Wokwi website `_ and the ``QEMU_PATH`` is the path to the QEMU binary. @@ -373,7 +367,7 @@ For example, to run the ``uart`` test using Wokwi, you would run: .. code-block:: bash - WOKWI_CLI_TOKEN= ./.github/scripts/tests_run.sh -s uart -t esp32c3 -W + WOKWI_CLI_TOKEN= ./.github/scripts/tests_run.sh -s uart -t esp32c3 -W And to run the ``uart`` test using QEMU, you would run: @@ -394,11 +388,10 @@ You can use the ``hello_world`` test suite as a starting point and the other tes A test suite contains the following files: -* ``test_.py``: The test file that contains the test cases. Required. -* ``.ino``: The sketch that will be tested. Required. -* ``ci.json``: The file that specifies how the test suite will be run in the CI system. Optional. +* ``test_.py``: Is the test file that contains the test cases. Required. +* ``.ino``: This is the sketch that will be tested. Required. +* ``ci.yml``: The file that specifies how the test suite will be run in the CI system. Optional. * ``diagram..json``: The diagram file that specifies the connections between the components in Wokwi. Optional. -* ``scenario.yaml``: The scenario file that specifies how Wokwi will interact with the components. Optional. * Any other files that are needed for the test suite. You can read more about the test python API in the `pytest-embedded documentation `_. @@ -406,10 +399,10 @@ For more information about the Unity testing framework, you can check the `Unity After creating the test suite, make sure to test it locally and run it in the CI system to ensure that it works as expected. -CI JSON File +CI YAML File ############ -The ``ci.json`` file is used to specify how the test suite and sketches will handled by the CI system. It can contain the following fields: +The ``ci.yml`` file is used to specify how the test suite and sketches will handled by the CI system. It can contain the following fields: * ``requires``: A list of configurations in ``sdkconfig`` that are required to run the test suite. The test suite will only run on the targets that have **ALL** the required configurations. By default, no configurations are required. @@ -420,16 +413,22 @@ The ``ci.json`` file is used to specify how the test suite and sketches will han specified in the ``requires`` field. This field is also valid for examples. * ``platforms``: A dictionary that specifies the supported platforms. The key is the platform name and the value is a boolean that specifies if the platform is supported. By default, all platforms are assumed to be supported. -* ``extra_tags``: A list of extra tags that the runner will require when running the test suite in hardware. By default, no extra tags are required. +* ``tags``: A list of tags that all runners will require when running the test suite in hardware. By default, no tags are required. +* ``soc_tags``: A dictionary that specifies SoC-specific tags required for hardware runners. The key is the target name and the value is a list + of tags. By default, no SoC-specific tags are required. * ``fqbn_append``: A string to be appended to the default FQBNs. By default, no string is appended. This has no effect if ``fqbn`` is specified. * ``fqbn``: A dictionary that specifies the FQBNs that will be used to compile the sketch. The key is the target name and the value is a list of FQBNs. The `default FQBNs `_ are used if this field is not specified. This overrides the default FQBNs and the ``fqbn_append`` field. +* ``libs``: A list of libraries that are required to run the test suite. The libraries will be installed automatically if they are not already present. + Libraries are installed using the ``arduino-cli lib install`` command, so you can specify libraries by name + version (e.g., ``AudioZero@1.0.0``) + or by URL (e.g., ``https://github.com/arduino-libraries/WiFi101.git``). + More information can be found in the `Arduino CLI documentation `_. -The ``wifi`` test suite is a good example of how to use the ``ci.json`` file: +The ``wifi`` test suite is a good example of how to use the ``ci.yml`` file: -.. literalinclude:: ../../tests/validation/wifi/ci.json - :language: json +.. literalinclude:: ../../tests/validation/wifi/ci.yml + :language: yaml Documentation Checks ^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/en/guides/core_compatibility.rst b/docs/en/guides/core_compatibility.rst index 8b6a8e79d26..a440b21e497 100644 --- a/docs/en/guides/core_compatibility.rst +++ b/docs/en/guides/core_compatibility.rst @@ -26,7 +26,7 @@ To ensure compatibility with both versions of the ESP32 Arduino core, developers Version Print ------------- -To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version: +To easily print the ESP32 Arduino core version at runtime, developers can use the ``ESP_ARDUINO_VERSION_STR`` macro. Below is an example of how to print the ESP32 Arduino core version: .. code-block:: cpp @@ -35,10 +35,10 @@ To easily print the ESP32 Arduino core version at runtime, developers can use th API Differences --------------- -Developers should be aware, that there may be API differences between major versions of the ESP32 Arduino core. For this we created a `Migration guide `_. to help developers transition from between major versions of the ESP32 Arduino core. +Developers should be aware, that there may be API differences between major versions of the ESP32 Arduino core. For this we created a `Migration guide `_. to help developers transition from between major versions of the ESP32 Arduino core. Library Testing --------------- We have added an External Library Test CI job, which tests external libraries with the latest version of the ESP32 Arduino core to help developers ensure compatibility with the latest version of the ESP32 Arduino core. -If you want to include your library in the External Library Test CI job, please follow the instructions in the `External Libraries Test `_. +If you want to include your library in the External Library Test CI job, please follow the instructions in the `External Libraries Test `_. diff --git a/docs/en/lib_builder.rst b/docs/en/lib_builder.rst index a8126c18edc..6c0693420ad 100644 --- a/docs/en/lib_builder.rst +++ b/docs/en/lib_builder.rst @@ -153,7 +153,9 @@ This build command will build for the ESP32-S3 target. You can specify other tar * esp32 * esp32c2 * esp32c3 +* esp32c5 * esp32c6 +* esp32c61 * esp32h2 * esp32p4 * esp32s2 @@ -211,7 +213,8 @@ Pre-Configuring the UI The UI can be pre-configured using command line arguments. The following arguments are available: - ``-t, --target ``: Comma-separated list of targets to be compiled. - Choose from: *all*, *esp32*, *esp32s2*, *esp32s3*, *esp32c2*, *esp32c3*, *esp32c6*, *esp32h2*. Default: all except *esp32c2*; + Choose from: *all*, *esp32*, *esp32c2*, *esp32c3*, *esp32c5*, *esp32c6*, *esp32c61*, *esp32h2*, *esp32s2*, *esp32s3*. + Default: all except *esp32c2* and *esp32c61*; - ``--copy, --no-copy``: Enable/disable copying the compiled libraries to ``arduino-esp32``. Enabled by default; - ``-c, --arduino-path ``: Path to ``arduino-esp32`` directory. Default: OS dependent; - ``-A, --arduino-branch ``: Branch of the ``arduino-esp32`` repository to be used. Default: set by the build script; diff --git a/docs/en/libraries.rst b/docs/en/libraries.rst index 07f0978be68..d9961d67b41 100644 --- a/docs/en/libraries.rst +++ b/docs/en/libraries.rst @@ -92,6 +92,29 @@ The Arduino ESP32 offers some unique APIs, described in this section: api/* +Matter APIs +----------- + +.. toctree:: + :maxdepth: 1 + :glob: + + matter/matter + matter/matter_ep + matter/ep_* + +OpenThread APIs +--------------- + +.. toctree:: + :maxdepth: 1 + :glob: + + openthread/openthread + openthread/openthread_cli + openthread/openthread_core + openthread/openthread_dataset + Zigbee APIs ----------- diff --git a/docs/en/matter/ep_color_light.rst b/docs/en/matter/ep_color_light.rst new file mode 100644 index 00000000000..17f9b47e817 --- /dev/null +++ b/docs/en/matter/ep_color_light.rst @@ -0,0 +1,212 @@ +################ +MatterColorLight +################ + +About +----- + +The ``MatterColorLight`` class provides a color light endpoint for Matter networks with RGB color control using the HSV color model. This endpoint implements the Matter lighting standard for full-color lighting control. + +**Features:** +* On/off control +* RGB color control with HSV color model +* State persistence support +* Callback support for state and color changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* RGB smart lights +* Color-changing lights +* Mood lighting +* Entertainment lighting control +* Smart home color automation + +API Reference +------------- + +Constructor +*********** + +MatterColorLight +^^^^^^^^^^^^^^^^ + +Creates a new Matter color light endpoint. + +.. code-block:: arduino + + MatterColorLight(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter color light endpoint with optional initial state and color. + +.. code-block:: arduino + + bool begin(bool initialState = false, espHsvColor_t colorHSV = {0, 254, 31}); + +* ``initialState`` - Initial on/off state (default: ``false`` = off) +* ``colorHSV`` - Initial HSV color (default: red 12% intensity HSV(0, 254, 31)) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter light events. + +.. code-block:: arduino + + void end(); + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the light. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +getOnOff +^^^^^^^^ + +Gets the current on/off state. + +.. code-block:: arduino + + bool getOnOff(); + +toggle +^^^^^^ + +Toggles the on/off state. + +.. code-block:: arduino + + bool toggle(); + +Color Control +************* + +setColorRGB +^^^^^^^^^^^ + +Sets the color using RGB values. + +.. code-block:: arduino + + bool setColorRGB(espRgbColor_t rgbColor); + +* ``rgbColor`` - RGB color structure with red, green, and blue values (0-255 each) + +getColorRGB +^^^^^^^^^^^ + +Gets the current color as RGB values. + +.. code-block:: arduino + + espRgbColor_t getColorRGB(); + +setColorHSV +^^^^^^^^^^^ + +Sets the color using HSV values. + +.. code-block:: arduino + + bool setColorHSV(espHsvColor_t hsvColor); + +* ``hsvColor`` - HSV color structure with hue (0-360), saturation (0-254), and value/brightness (0-254) + +getColorHSV +^^^^^^^^^^^ + +Gets the current color as HSV values. + +.. code-block:: arduino + + espHsvColor_t getColorHSV(); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback for when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState, espHsvColor_t newColor); + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback for on/off state changes. + +.. code-block:: arduino + + void onChangeOnOff(EndPointOnOffCB onChangeCB); + +onChangeColorHSV +^^^^^^^^^^^^^^^^ + +Sets a callback for color changes. + +.. code-block:: arduino + + void onChangeColorHSV(EndPointRGBColorCB onChangeCB); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the physical light state using current Matter internal state. + +.. code-block:: arduino + + void updateAccessory(); + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns current on/off state. + +.. code-block:: arduino + + operator bool(); + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns light on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example +------- + +Color Light +*********** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterColorLight/MatterColorLight.ino + :language: arduino diff --git a/docs/en/matter/ep_color_temperature_light.rst b/docs/en/matter/ep_color_temperature_light.rst new file mode 100644 index 00000000000..2a2cfe2395e --- /dev/null +++ b/docs/en/matter/ep_color_temperature_light.rst @@ -0,0 +1,258 @@ +########################### +MatterColorTemperatureLight +########################### + +About +----- + +The ``MatterColorTemperatureLight`` class provides a color temperature light endpoint for Matter networks with brightness and color temperature control. This endpoint implements the Matter lighting standard for lights that support color temperature adjustment (warm white to cool white). + +**Features:** +* On/off control +* Brightness level control (0-255) +* Color temperature control (100-500 mireds) +* State persistence support +* Callback support for state, brightness, and temperature changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Tunable white lights +* Color temperature adjustable lights +* Smart lighting with warm/cool control +* Circadian lighting +* Smart home lighting automation + +API Reference +------------- + +Constructor +*********** + +MatterColorTemperatureLight +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter color temperature light endpoint. + +.. code-block:: arduino + + MatterColorTemperatureLight(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter color temperature light endpoint with optional initial state, brightness, and color temperature. + +.. code-block:: arduino + + bool begin(bool initialState = false, uint8_t brightness = 64, uint16_t colorTemperature = 370); + +* ``initialState`` - Initial on/off state (default: ``false`` = off) +* ``brightness`` - Initial brightness level (0-255, default: 64 = 25%) +* ``colorTemperature`` - Initial color temperature in mireds (100-500, default: 370 = Soft White) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter light events. + +.. code-block:: arduino + + void end(); + +Constants +********* + +MAX_BRIGHTNESS +^^^^^^^^^^^^^^ + +Maximum brightness value (255). + +.. code-block:: arduino + + static const uint8_t MAX_BRIGHTNESS = 255; + +MAX_COLOR_TEMPERATURE +^^^^^^^^^^^^^^^^^^^^^ + +Maximum color temperature value (500 mireds = cool white). + +.. code-block:: arduino + + static const uint16_t MAX_COLOR_TEMPERATURE = 500; + +MIN_COLOR_TEMPERATURE +^^^^^^^^^^^^^^^^^^^^^ + +Minimum color temperature value (100 mireds = warm white). + +.. code-block:: arduino + + static const uint16_t MIN_COLOR_TEMPERATURE = 100; + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the light. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +getOnOff +^^^^^^^^ + +Gets the current on/off state. + +.. code-block:: arduino + + bool getOnOff(); + +toggle +^^^^^^ + +Toggles the on/off state. + +.. code-block:: arduino + + bool toggle(); + +Brightness Control +****************** + +setBrightness +^^^^^^^^^^^^^ + +Sets the brightness level. + +.. code-block:: arduino + + bool setBrightness(uint8_t newBrightness); + +* ``newBrightness`` - Brightness level (0-255) + +getBrightness +^^^^^^^^^^^^^ + +Gets the current brightness level. + +.. code-block:: arduino + + uint8_t getBrightness(); + +Color Temperature Control +************************* + +setColorTemperature +^^^^^^^^^^^^^^^^^^^ + +Sets the color temperature. + +.. code-block:: arduino + + bool setColorTemperature(uint16_t newTemperature); + +* ``newTemperature`` - Color temperature in mireds (100-500) + +**Note:** Color temperature is measured in mireds (micro reciprocal degrees). Lower values (100-200) are warm white, higher values (400-500) are cool white. + +getColorTemperature +^^^^^^^^^^^^^^^^^^^ + +Gets the current color temperature. + +.. code-block:: arduino + + uint16_t getColorTemperature(); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback for when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState, uint8_t newBrightness, uint16_t newTemperature); + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback for on/off state changes. + +.. code-block:: arduino + + void onChangeOnOff(EndPointOnOffCB onChangeCB); + +onChangeBrightness +^^^^^^^^^^^^^^^^^^ + +Sets a callback for brightness changes. + +.. code-block:: arduino + + void onChangeBrightness(EndPointBrightnessCB onChangeCB); + +onChangeColorTemperature +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for color temperature changes. + +.. code-block:: arduino + + void onChangeColorTemperature(EndPointTemperatureCB onChangeCB); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the physical light state using current Matter internal state. + +.. code-block:: arduino + + void updateAccessory(); + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns current on/off state. + +.. code-block:: arduino + + operator bool(); + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns light on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example +------- + +Color Temperature Light +*********************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterTemperatureLight/MatterTemperatureLight.ino + :language: arduino diff --git a/docs/en/matter/ep_contact_sensor.rst b/docs/en/matter/ep_contact_sensor.rst new file mode 100644 index 00000000000..cdb5e131936 --- /dev/null +++ b/docs/en/matter/ep_contact_sensor.rst @@ -0,0 +1,137 @@ +################### +MatterContactSensor +################### + +About +----- + +The ``MatterContactSensor`` class provides a contact sensor endpoint for Matter networks. This endpoint implements the Matter contact sensing standard for detecting open/closed states (e.g., doors, windows). + +**Features:** +* Contact state reporting (open/closed) +* Simple boolean state +* Read-only sensor (no control functionality) +* Automatic state updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Door/window sensors +* Contact switches +* Security systems +* Access control +* Smart home automation triggers + +API Reference +------------- + +Constructor +*********** + +MatterContactSensor +^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter contact sensor endpoint. + +.. code-block:: arduino + + MatterContactSensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter contact sensor endpoint with an initial contact state. + +.. code-block:: arduino + + bool begin(bool _contactState = false); + +* ``_contactState`` - Initial contact state (``true`` = closed, ``false`` = open, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter contact sensor events. + +.. code-block:: arduino + + void end(); + +Contact State Control +********************* + +setContact +^^^^^^^^^^ + +Sets the contact state. + +.. code-block:: arduino + + bool setContact(bool _contactState); + +* ``_contactState`` - Contact state (``true`` = closed, ``false`` = open) + +This function will return ``true`` if successful, ``false`` otherwise. + +getContact +^^^^^^^^^^ + +Gets the current contact state. + +.. code-block:: arduino + + bool getContact(); + +This function will return ``true`` if closed, ``false`` if open. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current contact state. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (mySensor) { + Serial.println("Contact is closed"); + } else { + Serial.println("Contact is open"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the contact state. + +.. code-block:: arduino + + void operator=(bool _contactState); + +Example: + +.. code-block:: arduino + + mySensor = true; // Set contact to closed + mySensor = false; // Set contact to open + +Example +------- + +Contact Sensor +************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterContactSensor/MatterContactSensor.ino + :language: arduino diff --git a/docs/en/matter/ep_dimmable_light.rst b/docs/en/matter/ep_dimmable_light.rst new file mode 100644 index 00000000000..7a442e4e4dc --- /dev/null +++ b/docs/en/matter/ep_dimmable_light.rst @@ -0,0 +1,236 @@ +################### +MatterDimmableLight +################### + +About +----- + +The ``MatterDimmableLight`` class provides a dimmable light endpoint for Matter networks. This endpoint implements the Matter lighting standard for lights with brightness control. + +**Features:** +* On/off control +* Brightness level control (0-255) +* State persistence support +* Callback support for state and brightness changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Dimmable smart lights +* Brightness control switches +* Smart home lighting automation +* Variable brightness lighting + +API Reference +------------- + +Constructor +*********** + +MatterDimmableLight +^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter dimmable light endpoint. + +.. code-block:: arduino + + MatterDimmableLight(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter dimmable light endpoint with optional initial state and brightness. + +.. code-block:: arduino + + bool begin(bool initialState = false, uint8_t brightness = 64); + +* ``initialState`` - Initial on/off state (``true`` = on, ``false`` = off, default: ``false``) +* ``brightness`` - Initial brightness level (0-255, default: 64 = 25%) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter light events. + +.. code-block:: arduino + + void end(); + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the light. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +* ``newState`` - New state (``true`` = on, ``false`` = off) + +This function will return ``true`` if successful, ``false`` otherwise. + +getOnOff +^^^^^^^^ + +Gets the current on/off state of the light. + +.. code-block:: arduino + + bool getOnOff(); + +This function will return ``true`` if the light is on, ``false`` if off. + +toggle +^^^^^^ + +Toggles the on/off state of the light. + +.. code-block:: arduino + + bool toggle(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Brightness Control +****************** + +setBrightness +^^^^^^^^^^^^^ + +Sets the brightness level of the light. + +.. code-block:: arduino + + bool setBrightness(uint8_t newBrightness); + +* ``newBrightness`` - New brightness level (0-255, where 0 = off, 255 = maximum brightness) + +This function will return ``true`` if successful, ``false`` otherwise. + +getBrightness +^^^^^^^^^^^^^ + +Gets the current brightness level of the light. + +.. code-block:: arduino + + uint8_t getBrightness(); + +This function will return the brightness level (0-255). + +Constants +********* + +MAX_BRIGHTNESS +^^^^^^^^^^^^^^ + +Maximum brightness value constant. + +.. code-block:: arduino + + static const uint8_t MAX_BRIGHTNESS = 255; + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current on/off state of the light. + +.. code-block:: arduino + + operator bool(); + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns the light on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback function to be called when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState, uint8_t newBrightness); + +* ``newState`` - New on/off state +* ``newBrightness`` - New brightness level (0-255) + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback function to be called when the on/off state changes. + +.. code-block:: arduino + + void onChangeOnOff(EndPointOnOffCB onChangeCB); + +* ``onChangeCB`` - Function to call when on/off state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState); + +onChangeBrightness +^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when the brightness level changes. + +.. code-block:: arduino + + void onChangeBrightness(EndPointBrightnessCB onChangeCB); + +* ``onChangeCB`` - Function to call when brightness changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(uint8_t newBrightness); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the state of the light using the current Matter Light internal state. + +.. code-block:: arduino + + void updateAccessory(); + +Example +------- + +Dimmable Light +************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterDimmableLight/MatterDimmableLight.ino + :language: arduino diff --git a/docs/en/matter/ep_dimmable_plugin.rst b/docs/en/matter/ep_dimmable_plugin.rst new file mode 100644 index 00000000000..f96e6adca64 --- /dev/null +++ b/docs/en/matter/ep_dimmable_plugin.rst @@ -0,0 +1,257 @@ +#################### +MatterDimmablePlugin +#################### + +About +----- + +The ``MatterDimmablePlugin`` class provides a dimmable plugin unit endpoint for Matter networks. This endpoint implements the Matter dimmable plugin standard for controlling power outlets, relays, and other dimmable devices with power level control. + +**Features:** +* On/off control +* Power level control (0-255) +* State persistence support +* Callback support for state and level changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Dimmable smart power outlets +* Variable power control +* Smart dimmer plugs +* Level-controlled device control + +API Reference +------------- + +Constructor +*********** + +MatterDimmablePlugin +^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter dimmable plugin endpoint. + +.. code-block:: arduino + + MatterDimmablePlugin(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter dimmable plugin endpoint with optional initial state and level. + +.. code-block:: arduino + + bool begin(bool initialState = false, uint8_t level = 64); + +* ``initialState`` - Initial on/off state (``true`` = on, ``false`` = off, default: ``false``) +* ``level`` - Initial power level (0-255, default: 64 = 25%) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter plugin events. + +.. code-block:: arduino + + void end(); + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the plugin. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +* ``newState`` - New state (``true`` = on, ``false`` = off) + +This function will return ``true`` if successful, ``false`` otherwise. + +getOnOff +^^^^^^^^ + +Gets the current on/off state of the plugin. + +.. code-block:: arduino + + bool getOnOff(); + +This function will return ``true`` if the plugin is on, ``false`` if off. + +toggle +^^^^^^ + +Toggles the on/off state of the plugin. + +.. code-block:: arduino + + bool toggle(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Level Control +************** + +setLevel +^^^^^^^^ + +Sets the power level of the plugin. + +.. code-block:: arduino + + bool setLevel(uint8_t newLevel); + +* ``newLevel`` - New power level (0-255, where 0 = off, 255 = maximum level) + +This function will return ``true`` if successful, ``false`` otherwise. + +getLevel +^^^^^^^^ + +Gets the current power level of the plugin. + +.. code-block:: arduino + + uint8_t getLevel(); + +This function will return the power level (0-255). + +Constants +********* + +MAX_LEVEL +^^^^^^^^^^ + +Maximum power level value constant. + +.. code-block:: arduino + + static const uint8_t MAX_LEVEL = 255; + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current on/off state of the plugin. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (myPlugin) { + Serial.println("Plugin is on"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns the plugin on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example: + +.. code-block:: arduino + + myPlugin = true; // Turn plugin on + myPlugin = false; // Turn plugin off + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback function to be called when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState, uint8_t newLevel); + +* ``newState`` - New on/off state (``true`` = on, ``false`` = off) +* ``newLevel`` - New power level (0-255) + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback function to be called when the on/off state changes. + +.. code-block:: arduino + + void onChangeOnOff(EndPointOnOffCB onChangeCB); + +* ``onChangeCB`` - Function to call when on/off state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState); + +* ``newState`` - New on/off state (``true`` = on, ``false`` = off) + +onChangeLevel +^^^^^^^^^^^^^ + +Sets a callback function to be called when the power level changes. + +.. code-block:: arduino + + void onChangeLevel(EndPointLevelCB onChangeCB); + +* ``onChangeCB`` - Function to call when level changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(uint8_t newLevel); + +* ``newLevel`` - New power level (0-255) + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the state of the plugin using the current Matter Plugin internal state. + +.. code-block:: arduino + + void updateAccessory(); + +This function will call the registered callback with the current state. + +Example +------- + +Dimmable Plugin +*************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterDimmablePlugin/MatterDimmablePlugin.ino + :language: arduino diff --git a/docs/en/matter/ep_enhanced_color_light.rst b/docs/en/matter/ep_enhanced_color_light.rst new file mode 100644 index 00000000000..3463c1c9d02 --- /dev/null +++ b/docs/en/matter/ep_enhanced_color_light.rst @@ -0,0 +1,303 @@ +######################## +MatterEnhancedColorLight +######################## + +About +----- + +The ``MatterEnhancedColorLight`` class provides an enhanced color light endpoint for Matter networks with full RGB color control, brightness, and color temperature. This endpoint implements the Matter lighting standard for advanced color lighting with all features. + +**Features:** +* On/off control +* RGB color control with HSV color model +* Brightness level control (0-255) +* Color temperature control (100-500 mireds) +* State persistence support +* Callback support for all parameter changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Full-featured RGB smart lights +* Advanced color and temperature control +* Mood lighting with all features +* Entertainment lighting +* Circadian lighting with color temperature +* Smart home advanced lighting automation + +API Reference +------------- + +Constructor +*********** + +MatterEnhancedColorLight +^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter enhanced color light endpoint. + +.. code-block:: arduino + + MatterEnhancedColorLight(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter enhanced color light endpoint with optional initial state, color, brightness, and color temperature. + +.. code-block:: arduino + + bool begin(bool initialState = false, espHsvColor_t colorHSV = {21, 216, 25}, uint8_t newBrightness = 25, uint16_t colorTemperature = 454); + +* ``initialState`` - Initial on/off state (default: ``false`` = off) +* ``colorHSV`` - Initial HSV color (default: HSV(21, 216, 25) = warm white) +* ``newBrightness`` - Initial brightness level (0-255, default: 25 = 10%) +* ``colorTemperature`` - Initial color temperature in mireds (100-500, default: 454 = Warm White) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter light events. + +.. code-block:: arduino + + void end(); + +Constants +********* + +MAX_BRIGHTNESS +^^^^^^^^^^^^^^ + +Maximum brightness value (255). + +.. code-block:: arduino + + static const uint8_t MAX_BRIGHTNESS = 255; + +MAX_COLOR_TEMPERATURE +^^^^^^^^^^^^^^^^^^^^^ + +Maximum color temperature value (500 mireds = cool white). + +.. code-block:: arduino + + static const uint16_t MAX_COLOR_TEMPERATURE = 500; + +MIN_COLOR_TEMPERATURE +^^^^^^^^^^^^^^^^^^^^^ + +Minimum color temperature value (100 mireds = warm white). + +.. code-block:: arduino + + static const uint16_t MIN_COLOR_TEMPERATURE = 100; + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the light. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +getOnOff +^^^^^^^^ + +Gets the current on/off state. + +.. code-block:: arduino + + bool getOnOff(); + +toggle +^^^^^^ + +Toggles the on/off state. + +.. code-block:: arduino + + bool toggle(); + +Color Control +************* + +setColorRGB +^^^^^^^^^^^ + +Sets the color using RGB values. + +.. code-block:: arduino + + bool setColorRGB(espRgbColor_t rgbColor); + +getColorRGB +^^^^^^^^^^^ + +Gets the current color as RGB values. + +.. code-block:: arduino + + espRgbColor_t getColorRGB(); + +setColorHSV +^^^^^^^^^^^ + +Sets the color using HSV values. + +.. code-block:: arduino + + bool setColorHSV(espHsvColor_t hsvColor); + +getColorHSV +^^^^^^^^^^^ + +Gets the current color as HSV values. + +.. code-block:: arduino + + espHsvColor_t getColorHSV(); + +Brightness Control +****************** + +setBrightness +^^^^^^^^^^^^^ + +Sets the brightness level. + +.. code-block:: arduino + + bool setBrightness(uint8_t newBrightness); + +getBrightness +^^^^^^^^^^^^^ + +Gets the current brightness level. + +.. code-block:: arduino + + uint8_t getBrightness(); + +Color Temperature Control +************************* + +setColorTemperature +^^^^^^^^^^^^^^^^^^^^ + +Sets the color temperature. + +.. code-block:: arduino + + bool setColorTemperature(uint16_t newTemperature); + +getColorTemperature +^^^^^^^^^^^^^^^^^^^ + +Gets the current color temperature. + +.. code-block:: arduino + + uint16_t getColorTemperature(); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback for when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState, espHsvColor_t newColor, uint8_t newBrightness, uint16_t newTemperature); + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback for on/off state changes. + +.. code-block:: arduino + + void onChangeOnOff(EndPointOnOffCB onChangeCB); + +onChangeBrightness +^^^^^^^^^^^^^^^^^^ + +Sets a callback for brightness changes. + +.. code-block:: arduino + + void onChangeBrightness(EndPointBrightnessCB onChangeCB); + +onChangeColorHSV +^^^^^^^^^^^^^^^^ + +Sets a callback for color changes. + +.. code-block:: arduino + + void onChangeColorHSV(EndPointRGBColorCB onChangeCB); + +onChangeColorTemperature +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for color temperature changes. + +.. code-block:: arduino + + void onChangeColorTemperature(EndPointTemperatureCB onChangeCB); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the physical light state using current Matter internal state. + +.. code-block:: arduino + + void updateAccessory(); + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns current on/off state. + +.. code-block:: arduino + + operator bool(); + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns light on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example +------- + +Enhanced Color Light +******************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterEnhancedColorLight/MatterEnhancedColorLight.ino + :language: arduino diff --git a/docs/en/matter/ep_fan.rst b/docs/en/matter/ep_fan.rst new file mode 100644 index 00000000000..8abc9d2933e --- /dev/null +++ b/docs/en/matter/ep_fan.rst @@ -0,0 +1,292 @@ +######### +MatterFan +######### + +About +----- + +The ``MatterFan`` class provides a fan endpoint for Matter networks with speed and mode control. This endpoint implements the Matter fan control standard. + +**Features:** +* On/off control +* Fan speed control (0-100%) +* Fan mode control (OFF, LOW, MEDIUM, HIGH, ON, AUTO, SMART) +* Fan mode sequence configuration +* Callback support for state, speed, and mode changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Smart ceiling fans +* Exhaust fans +* Ventilation fans +* Fan speed controllers +* HVAC fan control + +API Reference +------------- + +Constructor +*********** + +MatterFan +^^^^^^^^^ + +Creates a new Matter fan endpoint. + +.. code-block:: arduino + + MatterFan(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter fan endpoint with optional initial speed, mode, and mode sequence. + +.. code-block:: arduino + + bool begin(uint8_t percent = 0, FanMode_t fanMode = FAN_MODE_OFF, FanModeSequence_t fanModeSeq = FAN_MODE_SEQ_OFF_HIGH); + +* ``percent`` - Initial speed percentage (0-100, default: 0) +* ``fanMode`` - Initial fan mode (default: ``FAN_MODE_OFF``) +* ``fanModeSeq`` - Fan mode sequence configuration (default: ``FAN_MODE_SEQ_OFF_HIGH``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter fan events. + +.. code-block:: arduino + + void end(); + +Constants +********* + +MAX_SPEED +^^^^^^^^^ + +Maximum speed value (100%). + +.. code-block:: arduino + + static const uint8_t MAX_SPEED = 100; + +MIN_SPEED +^^^^^^^^^ + +Minimum speed value (1%). + +.. code-block:: arduino + + static const uint8_t MIN_SPEED = 1; + +OFF_SPEED +^^^^^^^^^ + +Speed value when fan is off (0%). + +.. code-block:: arduino + + static const uint8_t OFF_SPEED = 0; + +Fan Modes +********* + +FanMode_t +^^^^^^^^^ + +Fan mode enumeration: + +* ``FAN_MODE_OFF`` - Fan is off +* ``FAN_MODE_LOW`` - Low speed +* ``FAN_MODE_MEDIUM`` - Medium speed +* ``FAN_MODE_HIGH`` - High speed +* ``FAN_MODE_ON`` - Fan is on +* ``FAN_MODE_AUTO`` - Auto mode +* ``FAN_MODE_SMART`` - Smart mode + +Fan Mode Sequences +****************** + +FanModeSequence_t +^^^^^^^^^^^^^^^^^ + +Fan mode sequence enumeration: + +* ``FAN_MODE_SEQ_OFF_LOW_MED_HIGH`` - OFF, LOW, MEDIUM, HIGH +* ``FAN_MODE_SEQ_OFF_LOW_HIGH`` - OFF, LOW, HIGH +* ``FAN_MODE_SEQ_OFF_LOW_MED_HIGH_AUTO`` - OFF, LOW, MEDIUM, HIGH, AUTO +* ``FAN_MODE_SEQ_OFF_LOW_HIGH_AUTO`` - OFF, LOW, HIGH, AUTO +* ``FAN_MODE_SEQ_OFF_HIGH_AUTO`` - OFF, HIGH, AUTO +* ``FAN_MODE_SEQ_OFF_HIGH`` - OFF, HIGH + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the fan. + +.. code-block:: arduino + + bool setOnOff(bool newState, bool performUpdate = true); + +* ``newState`` - New state (``true`` = on, ``false`` = off) +* ``performUpdate`` - Perform update after setting (default: ``true``) + +getOnOff +^^^^^^^^ + +Gets the current on/off state. + +.. code-block:: arduino + + bool getOnOff(); + +toggle +^^^^^^ + +Toggles the on/off state. + +.. code-block:: arduino + + bool toggle(bool performUpdate = true); + +Speed Control +************* + +setSpeedPercent +^^^^^^^^^^^^^^^ + +Sets the fan speed percentage. + +.. code-block:: arduino + + bool setSpeedPercent(uint8_t newPercent, bool performUpdate = true); + +* ``newPercent`` - Speed percentage (0-100) +* ``performUpdate`` - Perform update after setting (default: ``true``) + +getSpeedPercent +^^^^^^^^^^^^^^^ + +Gets the current speed percentage. + +.. code-block:: arduino + + uint8_t getSpeedPercent(); + +Mode Control +************ + +setMode +^^^^^^^ + +Sets the fan mode. + +.. code-block:: arduino + + bool setMode(FanMode_t newMode, bool performUpdate = true); + +* ``newMode`` - Fan mode to set +* ``performUpdate`` - Perform update after setting (default: ``true``) + +getMode +^^^^^^^ + +Gets the current fan mode. + +.. code-block:: arduino + + FanMode_t getMode(); + +getFanModeString +^^^^^^^^^^^^^^^^ + +Gets a friendly string for the fan mode. + +.. code-block:: arduino + + static const char *getFanModeString(uint8_t mode); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback for when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(FanMode_t newMode, uint8_t newPercent); + +onChangeMode +^^^^^^^^^^^^ + +Sets a callback for mode changes. + +.. code-block:: arduino + + void onChangeMode(EndPointModeCB onChangeCB); + +onChangeSpeedPercent +^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for speed changes. + +.. code-block:: arduino + + void onChangeSpeedPercent(EndPointSpeedCB onChangeCB); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the physical fan state using current Matter internal state. + +.. code-block:: arduino + + void updateAccessory(); + +Operators +********* + +uint8_t operator +^^^^^^^^^^^^^^^^ + +Returns the current speed percentage. + +.. code-block:: arduino + + operator uint8_t(); + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the speed percentage. + +.. code-block:: arduino + + void operator=(uint8_t speedPercent); + +Example +------- + +Fan Control +*********** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterFan/MatterFan.ino + :language: arduino diff --git a/docs/en/matter/ep_generic_switch.rst b/docs/en/matter/ep_generic_switch.rst new file mode 100644 index 00000000000..902133dce4e --- /dev/null +++ b/docs/en/matter/ep_generic_switch.rst @@ -0,0 +1,94 @@ +################### +MatterGenericSwitch +################### + +About +----- + +The ``MatterGenericSwitch`` class provides a generic switch endpoint for Matter networks. This endpoint works as a smart button that can send click events to the Matter controller, enabling automation triggers. + +**Features:** +* Click event reporting to Matter controller +* Simple button functionality +* Automation trigger support +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Smart buttons +* Automation triggers +* Remote controls +* Scene activation buttons +* Event generators for smart home automation + +API Reference +------------- + +Constructor +*********** + +MatterGenericSwitch +^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter generic switch endpoint. + +.. code-block:: arduino + + MatterGenericSwitch(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter generic switch endpoint. + +.. code-block:: arduino + + bool begin(); + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter switch events. + +.. code-block:: arduino + + void end(); + +Event Generation +**************** + +click +^^^^^ + +Sends a click event to the Matter controller. + +.. code-block:: arduino + + void click(); + +This function sends a click event that can be used to trigger automations in smart home apps. The event is sent immediately and the Matter controller will receive it. + +**Usage Example:** + +When a physical button is pressed and released, call this function to send the click event: + +.. code-block:: arduino + + if (buttonReleased) { + mySwitch.click(); + Serial.println("Click event sent to Matter controller"); + } + +Example +------- + +Generic Switch (Smart Button) +***************************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterSmartButton/MatterSmartButton.ino + :language: arduino diff --git a/docs/en/matter/ep_humidity_sensor.rst b/docs/en/matter/ep_humidity_sensor.rst new file mode 100644 index 00000000000..d680f157c84 --- /dev/null +++ b/docs/en/matter/ep_humidity_sensor.rst @@ -0,0 +1,136 @@ +#################### +MatterHumiditySensor +#################### + +About +----- + +The ``MatterHumiditySensor`` class provides a humidity sensor endpoint for Matter networks. This endpoint implements the Matter humidity sensing standard for read-only humidity reporting. + +**Features:** +* Humidity measurement reporting (0-100%) +* 1/100th percent precision +* Read-only sensor (no control functionality) +* Automatic humidity updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Room humidity monitoring +* Weather stations +* HVAC systems +* Humidity logging +* Smart home climate monitoring + +API Reference +------------- + +Constructor +*********** + +MatterHumiditySensor +^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter humidity sensor endpoint. + +.. code-block:: arduino + + MatterHumiditySensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter humidity sensor endpoint with an initial humidity value. + +.. code-block:: arduino + + bool begin(double humidityPercent = 0.00); + +* ``humidityPercent`` - Initial humidity percentage (0.0-100.0, default: 0.00) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The implementation stores humidity with 1/100th percent precision internally. Values must be between 0.0 and 100.0. + +end +^^^ + +Stops processing Matter humidity sensor events. + +.. code-block:: arduino + + void end(); + +Humidity Control +**************** + +setHumidity +^^^^^^^^^^^ + +Sets the reported humidity percentage. + +.. code-block:: arduino + + bool setHumidity(double humidityPercent); + +* ``humidityPercent`` - Humidity percentage (0.0-100.0) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Humidity is stored with 1/100th percent precision. Values outside the 0.0-100.0 range will be rejected. + +getHumidity +^^^^^^^^^^^ + +Gets the current reported humidity percentage. + +.. code-block:: arduino + + double getHumidity(); + +This function will return the humidity percentage with 1/100th percent precision. + +Operators +********* + +double operator +^^^^^^^^^^^^^^^ + +Returns the current humidity percentage. + +.. code-block:: arduino + + operator double(); + +Example: + +.. code-block:: arduino + + double humidity = mySensor; // Get current humidity + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the humidity percentage. + +.. code-block:: arduino + + void operator=(double humidityPercent); + +Example: + +.. code-block:: arduino + + mySensor = 45.5; // Set humidity to 45.5% + +Example +------- + +Humidity Sensor +*************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterHumiditySensor/MatterHumiditySensor.ino + :language: arduino diff --git a/docs/en/matter/ep_occupancy_sensor.rst b/docs/en/matter/ep_occupancy_sensor.rst new file mode 100644 index 00000000000..456cafccefe --- /dev/null +++ b/docs/en/matter/ep_occupancy_sensor.rst @@ -0,0 +1,152 @@ +##################### +MatterOccupancySensor +##################### + +About +----- + +The ``MatterOccupancySensor`` class provides an occupancy sensor endpoint for Matter networks. This endpoint implements the Matter occupancy sensing standard for detecting occupied/unoccupied states (e.g., motion sensors, PIR sensors). + +**Features:** +* Occupancy state reporting (occupied/unoccupied) +* Multiple sensor type support (PIR, Ultrasonic, Physical Contact) +* Simple boolean state +* Read-only sensor (no control functionality) +* Automatic state updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Motion sensors (PIR) +* Occupancy detection +* Security systems +* Smart lighting automation +* Energy management (turn off lights when unoccupied) + +API Reference +------------- + +Constructor +*********** + +MatterOccupancySensor +^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter occupancy sensor endpoint. + +.. code-block:: arduino + + MatterOccupancySensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter occupancy sensor endpoint with an initial occupancy state and sensor type. + +.. code-block:: arduino + + bool begin(bool _occupancyState = false, OccupancySensorType_t _occupancySensorType = OCCUPANCY_SENSOR_TYPE_PIR); + +* ``_occupancyState`` - Initial occupancy state (``true`` = occupied, ``false`` = unoccupied, default: ``false``) +* ``_occupancySensorType`` - Sensor type (default: ``OCCUPANCY_SENSOR_TYPE_PIR``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter occupancy sensor events. + +.. code-block:: arduino + + void end(); + +Sensor Types +************ + +OccupancySensorType_t +^^^^^^^^^^^^^^^^^^^^^^ + +Occupancy sensor type enumeration: + +* ``OCCUPANCY_SENSOR_TYPE_PIR`` - Passive Infrared (PIR) sensor +* ``OCCUPANCY_SENSOR_TYPE_ULTRASONIC`` - Ultrasonic sensor +* ``OCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONIC`` - Combined PIR and Ultrasonic +* ``OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT`` - Physical contact sensor + +Occupancy State Control +*********************** + +setOccupancy +^^^^^^^^^^^^ + +Sets the occupancy state. + +.. code-block:: arduino + + bool setOccupancy(bool _occupancyState); + +* ``_occupancyState`` - Occupancy state (``true`` = occupied, ``false`` = unoccupied) + +This function will return ``true`` if successful, ``false`` otherwise. + +getOccupancy +^^^^^^^^^^^^ + +Gets the current occupancy state. + +.. code-block:: arduino + + bool getOccupancy(); + +This function will return ``true`` if occupied, ``false`` if unoccupied. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current occupancy state. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (mySensor) { + Serial.println("Room is occupied"); + } else { + Serial.println("Room is unoccupied"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the occupancy state. + +.. code-block:: arduino + + void operator=(bool _occupancyState); + +Example: + +.. code-block:: arduino + + mySensor = true; // Set to occupied + mySensor = false; // Set to unoccupied + +Example +------- + +Occupancy Sensor +**************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterOccupancySensor/MatterOccupancySensor.ino + :language: arduino diff --git a/docs/en/matter/ep_on_off_light.rst b/docs/en/matter/ep_on_off_light.rst new file mode 100644 index 00000000000..8d30851ea1b --- /dev/null +++ b/docs/en/matter/ep_on_off_light.rst @@ -0,0 +1,190 @@ +################ +MatterOnOffLight +################ + +About +----- + +The ``MatterOnOffLight`` class provides a simple on/off light endpoint for Matter networks. This endpoint implements the Matter lighting standard for basic light control without dimming or color features. + +**Features:** +* Simple on/off control +* State persistence support +* Callback support for state changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Simple smart lights +* On/off switches +* Basic lighting control +* Smart home automation + +API Reference +------------- + +Constructor +*********** + +MatterOnOffLight +^^^^^^^^^^^^^^^^ + +Creates a new Matter on/off light endpoint. + +.. code-block:: arduino + + MatterOnOffLight(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter on/off light endpoint with an optional initial state. + +.. code-block:: arduino + + bool begin(bool initialState = false); + +* ``initialState`` - Initial on/off state (``true`` = on, ``false`` = off, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter light events. This will just stop processing Light Matter events but won't remove the endpoint. + +.. code-block:: arduino + + void end(); + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the light. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +* ``newState`` - New state (``true`` = on, ``false`` = off) + +This function will return ``true`` if successful, ``false`` otherwise. + +getOnOff +^^^^^^^^ + +Gets the current on/off state of the light. + +.. code-block:: arduino + + bool getOnOff(); + +This function will return ``true`` if the light is on, ``false`` if off. + +toggle +^^^^^^ + +Toggles the on/off state of the light. + +.. code-block:: arduino + + bool toggle(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current on/off state of the light. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (myLight) { + Serial.println("Light is on"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns the light on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example: + +.. code-block:: arduino + + myLight = true; // Turn light on + myLight = false; // Turn light off + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback function to be called when the light state changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState); + +* ``newState`` - New on/off state (``true`` = on, ``false`` = off) + +The callback should return ``true`` if the change was handled successfully. + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback function to be called when the on/off state changes (same as ``onChange``). + +.. code-block:: arduino + + void onChangeOnOff(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when state changes + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the state of the light using the current Matter Light internal state. It is necessary to set a user callback function using ``onChange()`` to handle the physical light state. + +.. code-block:: arduino + + void updateAccessory(); + +This function will call the registered callback with the current state. + +Example +------- + +Basic On/Off Light +****************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterOnOffLight/MatterOnOffLight.ino + :language: arduino diff --git a/docs/en/matter/ep_on_off_plugin.rst b/docs/en/matter/ep_on_off_plugin.rst new file mode 100644 index 00000000000..dee2b040492 --- /dev/null +++ b/docs/en/matter/ep_on_off_plugin.rst @@ -0,0 +1,187 @@ +################# +MatterOnOffPlugin +################# + +About +----- + +The ``MatterOnOffPlugin`` class provides an on/off plugin unit endpoint for Matter networks. This endpoint implements the Matter on/off plugin standard for controlling power outlets, relays, and other on/off devices. + +**Features:** +* Simple on/off control +* State persistence support +* Callback support for state changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Smart power outlets +* Relay control +* Power switches +* Smart plugs +* On/off device control + +API Reference +------------- + +Constructor +*********** + +MatterOnOffPlugin +^^^^^^^^^^^^^^^^^ + +Creates a new Matter on/off plugin endpoint. + +.. code-block:: arduino + + MatterOnOffPlugin(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter on/off plugin endpoint with an optional initial state. + +.. code-block:: arduino + + bool begin(bool initialState = false); + +* ``initialState`` - Initial on/off state (``true`` = on, ``false`` = off, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter plugin events. + +.. code-block:: arduino + + void end(); + +On/Off Control +************** + +setOnOff +^^^^^^^^ + +Sets the on/off state of the plugin. + +.. code-block:: arduino + + bool setOnOff(bool newState); + +* ``newState`` - New state (``true`` = on, ``false`` = off) + +This function will return ``true`` if successful, ``false`` otherwise. + +getOnOff +^^^^^^^^ + +Gets the current on/off state of the plugin. + +.. code-block:: arduino + + bool getOnOff(); + +This function will return ``true`` if the plugin is on, ``false`` if off. + +toggle +^^^^^^ + +Toggles the on/off state of the plugin. + +.. code-block:: arduino + + bool toggle(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current on/off state of the plugin. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (myPlugin) { + Serial.println("Plugin is on"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Turns the plugin on or off. + +.. code-block:: arduino + + void operator=(bool state); + +Example: + +.. code-block:: arduino + + myPlugin = true; // Turn plugin on + myPlugin = false; // Turn plugin off + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback function to be called when the plugin state changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when state changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(bool newState); + +* ``newState`` - New on/off state (``true`` = on, ``false`` = off) + +onChangeOnOff +^^^^^^^^^^^^^ + +Sets a callback function to be called when the on/off state changes (same as ``onChange``). + +.. code-block:: arduino + + void onChangeOnOff(EndPointCB onChangeCB); + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the state of the plugin using the current Matter Plugin internal state. + +.. code-block:: arduino + + void updateAccessory(); + +This function will call the registered callback with the current state. + +Example +------- + +On/Off Plugin +************* + +.. literalinclude:: ../../../libraries/Matter/examples/MatterOnOffPlugin/MatterOnOffPlugin.ino + :language: arduino diff --git a/docs/en/matter/ep_pressure_sensor.rst b/docs/en/matter/ep_pressure_sensor.rst new file mode 100644 index 00000000000..713d43c5d7e --- /dev/null +++ b/docs/en/matter/ep_pressure_sensor.rst @@ -0,0 +1,133 @@ +#################### +MatterPressureSensor +#################### + +About +----- + +The ``MatterPressureSensor`` class provides a pressure sensor endpoint for Matter networks. This endpoint implements the Matter pressure sensing standard for read-only pressure reporting. + +**Features:** +* Pressure measurement reporting in hectopascals (hPa) +* Read-only sensor (no control functionality) +* Automatic pressure updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Atmospheric pressure monitoring +* Weather stations +* Barometric pressure sensors +* Pressure logging +* Smart home weather monitoring + +API Reference +------------- + +Constructor +*********** + +MatterPressureSensor +^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter pressure sensor endpoint. + +.. code-block:: arduino + + MatterPressureSensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter pressure sensor endpoint with an initial pressure value. + +.. code-block:: arduino + + bool begin(double pressure = 0.00); + +* ``pressure`` - Initial pressure in hectopascals (hPa, default: 0.00) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Typical atmospheric pressure at sea level is around 1013.25 hPa. Pressure values typically range from 950-1050 hPa. + +end +^^^ + +Stops processing Matter pressure sensor events. + +.. code-block:: arduino + + void end(); + +Pressure Control +**************** + +setPressure +^^^^^^^^^^^ + +Sets the reported pressure value. + +.. code-block:: arduino + + bool setPressure(double pressure); + +* ``pressure`` - Pressure in hectopascals (hPa) + +This function will return ``true`` if successful, ``false`` otherwise. + +getPressure +^^^^^^^^^^^ + +Gets the current reported pressure value. + +.. code-block:: arduino + + double getPressure(); + +This function will return the pressure in hectopascals (hPa). + +Operators +********* + +double operator +^^^^^^^^^^^^^^^ + +Returns the current pressure. + +.. code-block:: arduino + + operator double(); + +Example: + +.. code-block:: arduino + + double pressure = mySensor; // Get current pressure + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the pressure value. + +.. code-block:: arduino + + void operator=(double pressure); + +Example: + +.. code-block:: arduino + + mySensor = 1013.25; // Set pressure to 1013.25 hPa + +Example +------- + +Pressure Sensor +*************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterPressureSensor/MatterPressureSensor.ino + :language: arduino diff --git a/docs/en/matter/ep_rain_sensor.rst b/docs/en/matter/ep_rain_sensor.rst new file mode 100644 index 00000000000..53f150996cb --- /dev/null +++ b/docs/en/matter/ep_rain_sensor.rst @@ -0,0 +1,137 @@ +################### +MatterRainSensor +################### + +About +----- + +The ``MatterRainSensor`` class provides a rain sensor endpoint for Matter networks. This endpoint implements the Matter rain sensing standard for detecting rain presence (detected/not detected states). + +**Features:** +* Rain detection state reporting (detected/not detected) +* Simple boolean state +* Read-only sensor (no control functionality) +* Automatic state updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Weather monitoring systems +* Irrigation control systems +* Outdoor sensor networks +* Smart home automation triggers +* Rain detection for automated systems + +API Reference +------------- + +Constructor +*********** + +MatterRainSensor +^^^^^^^^^^^^^^^^ + +Creates a new Matter rain sensor endpoint. + +.. code-block:: arduino + + MatterRainSensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter rain sensor endpoint with an initial rain detection state. + +.. code-block:: arduino + + bool begin(bool _rainState = false); + +* ``_rainState`` - Initial rain detection state (``true`` = detected, ``false`` = not detected, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter rain sensor events. + +.. code-block:: arduino + + void end(); + +Rain Detection State Control +**************************** + +setRain +^^^^^^^ + +Sets the rain detection state. + +.. code-block:: arduino + + bool setRain(bool _rainState); + +* ``_rainState`` - Rain detection state (``true`` = detected, ``false`` = not detected) + +This function will return ``true`` if successful, ``false`` otherwise. + +getRain +^^^^^^^ + +Gets the current rain detection state. + +.. code-block:: arduino + + bool getRain(); + +This function will return ``true`` if rain is detected, ``false`` if not detected. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current rain detection state. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (mySensor) { + Serial.println("Rain is detected"); + } else { + Serial.println("Rain is not detected"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the rain detection state. + +.. code-block:: arduino + + void operator=(bool _rainState); + +Example: + +.. code-block:: arduino + + mySensor = true; // Set rain detection to detected + mySensor = false; // Set rain detection to not detected + +Example +------- + +Rain Sensor +*********** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterRainSensor/MatterRainSensor.ino + :language: arduino diff --git a/docs/en/matter/ep_temperature_controlled_cabinet.rst b/docs/en/matter/ep_temperature_controlled_cabinet.rst new file mode 100644 index 00000000000..f374d878992 --- /dev/null +++ b/docs/en/matter/ep_temperature_controlled_cabinet.rst @@ -0,0 +1,296 @@ +################################## +MatterTemperatureControlledCabinet +################################## + +About +----- + +The ``MatterTemperatureControlledCabinet`` class provides a temperature controlled cabinet endpoint for Matter networks. This endpoint implements the Matter temperature control standard for managing temperature setpoints with min/max limits, step control (always enabled), or temperature level support. + +**Features:** + +* Two initialization modes: + + - **Temperature Number Mode** (``begin(tempSetpoint, minTemp, maxTemp, step)``): Temperature setpoint control with min/max limits and step control + - **Temperature Level Mode** (``begin(supportedLevels, levelCount, selectedLevel)``): Temperature level control with array of supported levels + +* 1/100th degree Celsius precision (for temperature_number mode) +* Min/max temperature limits with validation (temperature_number mode) +* Temperature step control (temperature_number mode, always enabled) +* Temperature level array support (temperature_level mode) +* Automatic setpoint validation against limits +* Feature validation - methods return errors if called with wrong feature mode +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Important:** The ``temperature_number`` and ``temperature_level`` features are **mutually exclusive**. Only one can be enabled at a time. Use ``begin(tempSetpoint, minTemp, maxTemp, step)`` for temperature_number mode or ``begin(supportedLevels, levelCount, selectedLevel)`` for temperature_level mode. + +**Use Cases:** + +* Refrigerators and freezers +* Wine coolers +* Medical storage cabinets +* Laboratory equipment +* Food storage systems +* Temperature-controlled storage units + +API Reference +------------- + +Constructor +*********** + +MatterTemperatureControlledCabinet +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter temperature controlled cabinet endpoint. + +.. code-block:: arduino + + MatterTemperatureControlledCabinet(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter temperature controlled cabinet endpoint with **temperature_number** feature mode. This enables temperature setpoint control with min/max limits and optional step. + +**Note:** This mode is mutually exclusive with temperature_level mode. Use ``begin(supportedLevels, levelCount, selectedLevel)`` for temperature level control. + +.. code-block:: arduino + + bool begin(double tempSetpoint = 0.00, double minTemperature = -10.0, double maxTemperature = 32.0, double step = 0.50); + +* ``tempSetpoint`` - Initial temperature setpoint in Celsius (default: 0.00) +* ``minTemperature`` - Minimum allowed temperature in Celsius (default: -10.0) +* ``maxTemperature`` - Maximum allowed temperature in Celsius (default: 32.0) +* ``step`` - Initial temperature step value in Celsius (default: 0.50) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The implementation stores temperature with 1/100th degree Celsius precision internally. The temperature_step feature is always enabled for temperature_number mode, allowing ``setStep()`` to be called later even if step is not provided in ``begin()``. + +begin (overloaded) +^^^^^^^^^^^^^^^^^^ + +Initializes the Matter temperature controlled cabinet endpoint with **temperature_level** feature mode. This enables temperature level control with an array of supported levels. + +**Note:** This mode is mutually exclusive with temperature_number mode. Use ``begin(tempSetpoint, minTemp, maxTemp, step)`` for temperature setpoint control. + +.. code-block:: arduino + + bool begin(uint8_t *supportedLevels, uint16_t levelCount, uint8_t selectedLevel = 0); + +* ``supportedLevels`` - Pointer to array of temperature level values (uint8_t, 0-255) +* ``levelCount`` - Number of levels in the array (maximum: 16) +* ``selectedLevel`` - Initial selected temperature level (default: 0) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The maximum number of supported levels is 16 (defined by ``temperature_control::k_max_temp_level_count``). The array is copied internally, so it does not need to remain valid after the function returns. This method uses a custom endpoint implementation that properly supports the temperature_level feature. + +end +^^^ + +Stops processing Matter temperature controlled cabinet events. + +.. code-block:: arduino + + void end(); + +Temperature Setpoint Control +**************************** + +setTemperatureSetpoint +^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the temperature setpoint value. The setpoint will be validated against min/max limits. + +**Requires:** temperature_number feature mode (use ``begin()``) + +.. code-block:: arduino + + bool setTemperatureSetpoint(double temperature); + +* ``temperature`` - Temperature setpoint in Celsius + +This function will return ``true`` if successful, ``false`` otherwise (e.g., if temperature is out of range or wrong feature mode). + +**Note:** Temperature is stored with 1/100th degree Celsius precision. The setpoint must be within the min/max temperature range. This method will return ``false`` and log an error if called when using temperature_level mode. + +getTemperatureSetpoint +^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current temperature setpoint value. + +.. code-block:: arduino + + double getTemperatureSetpoint(); + +This function will return the temperature setpoint in Celsius with 1/100th degree precision. + +Min/Max Temperature Control +**************************** + +setMinTemperature +^^^^^^^^^^^^^^^^^ + +Sets the minimum allowed temperature. + +**Requires:** temperature_number feature mode (use ``begin()``) + +.. code-block:: arduino + + bool setMinTemperature(double temperature); + +* ``temperature`` - Minimum temperature in Celsius + +This function will return ``true`` if successful, ``false`` otherwise. Will return ``false`` and log an error if called when using temperature_level mode. + +getMinTemperature +^^^^^^^^^^^^^^^^^ + +Gets the minimum allowed temperature. + +.. code-block:: arduino + + double getMinTemperature(); + +This function will return the minimum temperature in Celsius with 1/100th degree precision. + +setMaxTemperature +^^^^^^^^^^^^^^^^^ + +Sets the maximum allowed temperature. + +**Requires:** temperature_number feature mode (use ``begin()``) + +.. code-block:: arduino + + bool setMaxTemperature(double temperature); + +* ``temperature`` - Maximum temperature in Celsius + +This function will return ``true`` if successful, ``false`` otherwise. Will return ``false`` and log an error if called when using temperature_level mode. + +getMaxTemperature +^^^^^^^^^^^^^^^^^ + +Gets the maximum allowed temperature. + +.. code-block:: arduino + + double getMaxTemperature(); + +This function will return the maximum temperature in Celsius with 1/100th degree precision. + +Temperature Step Control +************************* + +setStep +^^^^^^^ + +Sets the temperature step value. + +**Requires:** temperature_number feature mode (use ``begin()``) + +.. code-block:: arduino + + bool setStep(double step); + +* ``step`` - Temperature step value in Celsius + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The temperature_step feature is always enabled when using temperature_number mode, so this method can be called at any time to set or change the step value, even if step was not provided in ``begin()``. This method will return ``false`` and log an error if called when using temperature_level mode. + +getStep +^^^^^^^ + +Gets the current temperature step value. + +.. code-block:: arduino + + double getStep(); + +This function will return the temperature step in Celsius with 1/100th degree precision. + +Temperature Level Control (Optional) +************************************* + +setSelectedTemperatureLevel +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the selected temperature level. + +**Requires:** temperature_level feature mode (use ``begin(supportedLevels, levelCount, selectedLevel)``) + +.. code-block:: arduino + + bool setSelectedTemperatureLevel(uint8_t level); + +* ``level`` - Temperature level (0-255) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Temperature level and temperature number features are mutually exclusive. This method will return ``false`` and log an error if called when using temperature_number mode. + +getSelectedTemperatureLevel +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current selected temperature level. + +.. code-block:: arduino + + uint8_t getSelectedTemperatureLevel(); + +This function will return the selected temperature level (0-255). + +setSupportedTemperatureLevels +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the supported temperature levels array. + +**Requires:** temperature_level feature mode (use ``begin(supportedLevels, levelCount, selectedLevel)``) + +.. code-block:: arduino + + bool setSupportedTemperatureLevels(uint8_t *levels, uint16_t count); + +* ``levels`` - Pointer to array of temperature level values (array is copied internally) +* ``count`` - Number of levels in the array (maximum: 16) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The maximum number of supported levels is 16. The array is copied internally, so it does not need to remain valid after the function returns. This method will return ``false`` and log an error if called when using temperature_number mode or if count exceeds the maximum. + +getSupportedTemperatureLevelsCount +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the count of supported temperature levels. + +.. code-block:: arduino + + uint16_t getSupportedTemperatureLevelsCount(); + +This function will return the number of supported temperature levels. + +Example +------- + +Temperature Controlled Cabinet +****************************** + +The example demonstrates the temperature_number mode with dynamic temperature updates. The temperature setpoint automatically cycles between the minimum and maximum limits every 1 second using the configured step value, allowing Matter controllers to observe real-time changes. The example also monitors and logs when the initial setpoint is reached or overpassed in each direction. + +.. literalinclude:: ../../../libraries/Matter/examples/MatterTemperatureControlledCabinet/MatterTemperatureControlledCabinet.ino + :language: arduino + +Temperature Controlled Cabinet (Level Mode) +******************************************** + +A separate example demonstrates the temperature_level mode with dynamic level updates. The temperature level automatically cycles through all supported levels every 1 second in both directions (increasing and decreasing), allowing Matter controllers to observe real-time changes. The example also monitors and logs when the initial level is reached or overpassed in each direction. + +See ``MatterTemperatureControlledCabinetLevels`` example for the temperature level mode implementation. diff --git a/docs/en/matter/ep_temperature_sensor.rst b/docs/en/matter/ep_temperature_sensor.rst new file mode 100644 index 00000000000..0202ba54395 --- /dev/null +++ b/docs/en/matter/ep_temperature_sensor.rst @@ -0,0 +1,136 @@ +####################### +MatterTemperatureSensor +####################### + +About +----- + +The ``MatterTemperatureSensor`` class provides a temperature sensor endpoint for Matter networks. This endpoint implements the Matter temperature sensing standard for read-only temperature reporting. + +**Features:** +* Temperature measurement reporting in Celsius +* 1/100th degree Celsius precision +* Read-only sensor (no control functionality) +* Automatic temperature updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Room temperature monitoring +* Weather stations +* HVAC systems +* Temperature logging +* Smart home climate monitoring + +API Reference +------------- + +Constructor +*********** + +MatterTemperatureSensor +^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter temperature sensor endpoint. + +.. code-block:: arduino + + MatterTemperatureSensor(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter temperature sensor endpoint with an initial temperature value. + +.. code-block:: arduino + + bool begin(double temperature = 0.00); + +* ``temperature`` - Initial temperature in Celsius (default: 0.00) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** The implementation stores temperature with 1/100th degree Celsius precision internally. + +end +^^^ + +Stops processing Matter temperature sensor events. + +.. code-block:: arduino + + void end(); + +Temperature Control +******************* + +setTemperature +^^^^^^^^^^^^^^ + +Sets the reported temperature value. + +.. code-block:: arduino + + bool setTemperature(double temperature); + +* ``temperature`` - Temperature in Celsius + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Temperature is stored with 1/100th degree Celsius precision. The valid range is typically -273.15°C (absolute zero) to 327.67°C. + +getTemperature +^^^^^^^^^^^^^^ + +Gets the current reported temperature value. + +.. code-block:: arduino + + double getTemperature(); + +This function will return the temperature in Celsius with 1/100th degree precision. + +Operators +********* + +double operator +^^^^^^^^^^^^^^^ + +Returns the current temperature. + +.. code-block:: arduino + + operator double(); + +Example: + +.. code-block:: arduino + + double temp = mySensor; // Get current temperature + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the temperature value. + +.. code-block:: arduino + + void operator=(double temperature); + +Example: + +.. code-block:: arduino + + mySensor = 25.5; // Set temperature to 25.5°C + +Example +------- + +Temperature Sensor +****************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterTemperatureSensor/MatterTemperatureSensor.ino + :language: arduino diff --git a/docs/en/matter/ep_thermostat.rst b/docs/en/matter/ep_thermostat.rst new file mode 100644 index 00000000000..1a736383443 --- /dev/null +++ b/docs/en/matter/ep_thermostat.rst @@ -0,0 +1,310 @@ +################ +MatterThermostat +################ + +About +----- + +The ``MatterThermostat`` class provides a thermostat endpoint for Matter networks with temperature control, setpoints, and multiple operating modes. This endpoint implements the Matter thermostat standard. + +**Features:** +* Multiple operating modes (OFF, HEAT, COOL, AUTO, etc.) +* Heating and cooling setpoint control +* Local temperature reporting +* Automatic temperature regulation +* Deadband control for AUTO mode +* Callback support for mode, temperature, and setpoint changes +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* HVAC systems +* Smart thermostats +* Temperature control systems +* Climate control automation +* Energy management systems + +API Reference +------------- + +Constructor +*********** + +MatterThermostat +^^^^^^^^^^^^^^^^ + +Creates a new Matter thermostat endpoint. + +.. code-block:: arduino + + MatterThermostat(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter thermostat endpoint with control sequence and auto mode settings. + +.. code-block:: arduino + + bool begin(ControlSequenceOfOperation_t controlSequence = THERMOSTAT_SEQ_OP_COOLING, ThermostatAutoMode_t autoMode = THERMOSTAT_AUTO_MODE_DISABLED); + +* ``controlSequence`` - Control sequence of operation (default: ``THERMOSTAT_SEQ_OP_COOLING``) +* ``autoMode`` - Auto mode enabled/disabled (default: ``THERMOSTAT_AUTO_MODE_DISABLED``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter thermostat events. + +.. code-block:: arduino + + void end(); + +Control Sequences +***************** + +ControlSequenceOfOperation_t +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Control sequence enumeration: + +* ``THERMOSTAT_SEQ_OP_COOLING`` - Cooling only +* ``THERMOSTAT_SEQ_OP_COOLING_REHEAT`` - Cooling with reheat +* ``THERMOSTAT_SEQ_OP_HEATING`` - Heating only +* ``THERMOSTAT_SEQ_OP_HEATING_REHEAT`` - Heating with reheat +* ``THERMOSTAT_SEQ_OP_COOLING_HEATING`` - Cooling and heating +* ``THERMOSTAT_SEQ_OP_COOLING_HEATING_REHEAT`` - Cooling and heating with reheat + +Thermostat Modes +**************** + +ThermostatMode_t +^^^^^^^^^^^^^^^^ + +Thermostat mode enumeration: + +* ``THERMOSTAT_MODE_OFF`` - Off +* ``THERMOSTAT_MODE_AUTO`` - Auto mode +* ``THERMOSTAT_MODE_COOL`` - Cooling mode +* ``THERMOSTAT_MODE_HEAT`` - Heating mode +* ``THERMOSTAT_MODE_EMERGENCY_HEAT`` - Emergency heat +* ``THERMOSTAT_MODE_PRECOOLING`` - Precooling +* ``THERMOSTAT_MODE_FAN_ONLY`` - Fan only +* ``THERMOSTAT_MODE_DRY`` - Dry mode +* ``THERMOSTAT_MODE_SLEEP`` - Sleep mode + +Mode Control +************ + +setMode +^^^^^^^ + +Sets the thermostat mode. + +.. code-block:: arduino + + bool setMode(ThermostatMode_t mode); + +getMode +^^^^^^^ + +Gets the current thermostat mode. + +.. code-block:: arduino + + ThermostatMode_t getMode(); + +getThermostatModeString +^^^^^^^^^^^^^^^^^^^^^^^ + +Gets a friendly string for the thermostat mode. + +.. code-block:: arduino + + static const char *getThermostatModeString(uint8_t mode); + +Temperature Control +******************* + +setLocalTemperature +^^^^^^^^^^^^^^^^^^^ + +Sets the local temperature reading. + +.. code-block:: arduino + + bool setLocalTemperature(double temperature); + +* ``temperature`` - Temperature in Celsius + +getLocalTemperature +^^^^^^^^^^^^^^^^^^^ + +Gets the current local temperature. + +.. code-block:: arduino + + double getLocalTemperature(); + +Setpoint Control +**************** + +setCoolingHeatingSetpoints +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets both cooling and heating setpoints. + +.. code-block:: arduino + + bool setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCoolingTemperature); + +* ``_setpointHeatingTemperature`` - Heating setpoint in Celsius (or 0xffff to keep current) +* ``_setpointCoolingTemperature`` - Cooling setpoint in Celsius (or 0xffff to keep current) + +**Note:** Heating setpoint must be lower than cooling setpoint. In AUTO mode, cooling setpoint must be at least 2.5°C higher than heating setpoint (deadband). + +setHeatingSetpoint +^^^^^^^^^^^^^^^^^^ + +Sets the heating setpoint. + +.. code-block:: arduino + + bool setHeatingSetpoint(double _setpointHeatingTemperature); + +getHeatingSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the heating setpoint. + +.. code-block:: arduino + + double getHeatingSetpoint(); + +setCoolingSetpoint +^^^^^^^^^^^^^^^^^^ + +Sets the cooling setpoint. + +.. code-block:: arduino + + bool setCoolingSetpoint(double _setpointCoolingTemperature); + +getCoolingSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the cooling setpoint. + +.. code-block:: arduino + + double getCoolingSetpoint(); + +Setpoint Limits +*************** + +getMinHeatSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the minimum heating setpoint limit. + +.. code-block:: arduino + + float getMinHeatSetpoint(); + +getMaxHeatSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the maximum heating setpoint limit. + +.. code-block:: arduino + + float getMaxHeatSetpoint(); + +getMinCoolSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the minimum cooling setpoint limit. + +.. code-block:: arduino + + float getMinCoolSetpoint(); + +getMaxCoolSetpoint +^^^^^^^^^^^^^^^^^^ + +Gets the maximum cooling setpoint limit. + +.. code-block:: arduino + + float getMaxCoolSetpoint(); + +getDeadBand +^^^^^^^^^^^ + +Gets the deadband value (minimum difference between heating and cooling setpoints in AUTO mode). + +.. code-block:: arduino + + float getDeadBand(); + +Event Handling +************** + +onChange +^^^^^^^^ + +Sets a callback for when any parameter changes. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +onChangeMode +^^^^^^^^^^^^ + +Sets a callback for mode changes. + +.. code-block:: arduino + + void onChangeMode(EndPointModeCB onChangeCB); + +onChangeLocalTemperature +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for local temperature changes. + +.. code-block:: arduino + + void onChangeLocalTemperature(EndPointTemperatureCB onChangeCB); + +onChangeCoolingSetpoint +^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for cooling setpoint changes. + +.. code-block:: arduino + + void onChangeCoolingSetpoint(EndPointCoolingSetpointCB onChangeCB); + +onChangeHeatingSetpoint +^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback for heating setpoint changes. + +.. code-block:: arduino + + void onChangeHeatingSetpoint(EndPointHeatingSetpointCB onChangeCB); + +Example +------- + +Thermostat +********** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterThermostat/MatterThermostat.ino + :language: arduino diff --git a/docs/en/matter/ep_water_freeze_detector.rst b/docs/en/matter/ep_water_freeze_detector.rst new file mode 100644 index 00000000000..e24ee344ef5 --- /dev/null +++ b/docs/en/matter/ep_water_freeze_detector.rst @@ -0,0 +1,137 @@ +########################### +MatterWaterFreezeDetector +########################### + +About +----- + +The ``MatterWaterFreezeDetector`` class provides a water freeze detector endpoint for Matter networks. This endpoint implements the Matter water freeze detection standard for detecting water freeze conditions (detected/not detected states). + +**Features:** +* Water freeze detection state reporting (detected/not detected) +* Simple boolean state +* Read-only sensor (no control functionality) +* Automatic state updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Water pipe freeze monitoring +* Outdoor water system protection +* HVAC freeze detection +* Smart home automation triggers +* Preventative maintenance systems + +API Reference +------------- + +Constructor +*********** + +MatterWaterFreezeDetector +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter water freeze detector endpoint. + +.. code-block:: arduino + + MatterWaterFreezeDetector(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter water freeze detector endpoint with an initial freeze detection state. + +.. code-block:: arduino + + bool begin(bool _freezeState = false); + +* ``_freezeState`` - Initial water freeze detection state (``true`` = detected, ``false`` = not detected, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter water freeze detector events. + +.. code-block:: arduino + + void end(); + +Water Freeze Detection State Control +************************************ + +setFreeze +^^^^^^^^^ + +Sets the water freeze detection state. + +.. code-block:: arduino + + bool setFreeze(bool _freezeState); + +* ``_freezeState`` - Water freeze detection state (``true`` = detected, ``false`` = not detected) + +This function will return ``true`` if successful, ``false`` otherwise. + +getFreeze +^^^^^^^^^ + +Gets the current water freeze detection state. + +.. code-block:: arduino + + bool getFreeze(); + +This function will return ``true`` if water freeze is detected, ``false`` if not detected. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current water freeze detection state. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (myDetector) { + Serial.println("Water freeze is detected"); + } else { + Serial.println("Water freeze is not detected"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the water freeze detection state. + +.. code-block:: arduino + + void operator=(bool _freezeState); + +Example: + +.. code-block:: arduino + + myDetector = true; // Set water freeze detection to detected + myDetector = false; // Set water freeze detection to not detected + +Example +------- + +Water Freeze Detector +********************* + +.. literalinclude:: ../../../libraries/Matter/examples/MatterWaterFreezeDetector/MatterWaterFreezeDetector.ino + :language: arduino diff --git a/docs/en/matter/ep_water_leak_detector.rst b/docs/en/matter/ep_water_leak_detector.rst new file mode 100644 index 00000000000..e68d984fe13 --- /dev/null +++ b/docs/en/matter/ep_water_leak_detector.rst @@ -0,0 +1,137 @@ +########################## +MatterWaterLeakDetector +########################## + +About +----- + +The ``MatterWaterLeakDetector`` class provides a water leak detector endpoint for Matter networks. This endpoint implements the Matter water leak detection standard for detecting water leak conditions (detected/not detected states). + +**Features:** +* Water leak detection state reporting (detected/not detected) +* Simple boolean state +* Read-only sensor (no control functionality) +* Automatic state updates +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Use Cases:** +* Water leak monitoring +* Basement flood detection +* Appliance leak detection +* Smart home automation triggers +* Preventative maintenance systems + +API Reference +------------- + +Constructor +*********** + +MatterWaterLeakDetector +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter water leak detector endpoint. + +.. code-block:: arduino + + MatterWaterLeakDetector(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter water leak detector endpoint with an initial leak detection state. + +.. code-block:: arduino + + bool begin(bool _leakState = false); + +* ``_leakState`` - Initial water leak detection state (``true`` = detected, ``false`` = not detected, default: ``false``) + +This function will return ``true`` if successful, ``false`` otherwise. + +end +^^^ + +Stops processing Matter water leak detector events. + +.. code-block:: arduino + + void end(); + +Water Leak Detection State Control +*********************************** + +setLeak +^^^^^^^^ + +Sets the water leak detection state. + +.. code-block:: arduino + + bool setLeak(bool _leakState); + +* ``_leakState`` - Water leak detection state (``true`` = detected, ``false`` = not detected) + +This function will return ``true`` if successful, ``false`` otherwise. + +getLeak +^^^^^^^ + +Gets the current water leak detection state. + +.. code-block:: arduino + + bool getLeak(); + +This function will return ``true`` if water leak is detected, ``false`` if not detected. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns the current water leak detection state. + +.. code-block:: arduino + + operator bool(); + +Example: + +.. code-block:: arduino + + if (myDetector) { + Serial.println("Water leak is detected"); + } else { + Serial.println("Water leak is not detected"); + } + +Assignment operator +^^^^^^^^^^^^^^^^^^^ + +Sets the water leak detection state. + +.. code-block:: arduino + + void operator=(bool _leakState); + +Example: + +.. code-block:: arduino + + myDetector = true; // Set water leak detection to detected + myDetector = false; // Set water leak detection to not detected + +Example +------- + +Water Leak Detector +******************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterWaterLeakDetector/MatterWaterLeakDetector.ino + :language: arduino diff --git a/docs/en/matter/matter.rst b/docs/en/matter/matter.rst new file mode 100644 index 00000000000..2465a2c7026 --- /dev/null +++ b/docs/en/matter/matter.rst @@ -0,0 +1,222 @@ +###### +Matter +###### + +About +----- + +The Matter library provides support for creating Matter-compatible devices including: + +* Support for Wi-Fi and Thread connectivity +* Matter commissioning via QR code or manual pairing code +* Multiple endpoint types for various device categories +* Event monitoring and callback support +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Smart home ecosystem compatibility + +The Matter library is built on top of `ESP Matter SDK `_ and provides a high-level Arduino-style interface for creating Matter devices. + +Matter Protocol Overview +************************ + +Matter (formerly Project CHIP - Connected Home over IP) is an open-source connectivity standard for smart home devices. It enables seamless communication between devices from different manufacturers, allowing them to work together within a single ecosystem. + +**Key Features:** + +* **Multi-Protocol Support**: Works over Wi-Fi, Thread, and Ethernet +* **Interoperability**: Devices from different manufacturers work together +* **Security**: Built-in security features including encryption and authentication +* **Local Control**: Devices can communicate locally without requiring cloud connectivity +* **Simple Setup**: Easy commissioning via QR code or pairing code + +Matter Network Topology +*********************** + +.. code-block:: text + + ┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐ + │ Matter Hub │◄─────►│ Wi-Fi Router │◄─────►│ ESP Thread │ + │ (HomePod etc) │ │ │ │ Border Router │ + └─────────────────┘ └──────────────────┘ └─────────────────┘ + │ │ │ + │ ▼ │ + │ ┌─────────────────┐ │ + │ │ Matter Device │ │ + │ │ (via Wi-Fi) │ │ + │ └─────────────────┘ │ + │ │ + ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ + │ Matter Device │ │ Matter Device │ + │ (via Wi-Fi) │ │ (via Thread) │ + └─────────────────┘ └─────────────────┘ + + +**Network Interfaces:** + +* **Wi-Fi**: High-bandwidth connection for devices that require constant power +* **Thread**: Low-power mesh networking for battery-operated devices +* **Ethernet**: Wired connection for stationary devices + +Matter Library Structure +------------------------ + +**The library is split into three main components:** + +* ``Matter``: The main class that manages the Matter network and device commissioning +* ``MatterEndPoint``: The base class for all Matter endpoints, which provides common functionality for all endpoint types +* ``Specific endpoint classes``: The classes for all Matter endpoints, which provides the specific functionality for each endpoint type + +Matter +****** + +The ``Matter`` class is the main entry point for all Matter operations. It serves as the central manager that handles: + +* **Device Commissioning**: Managing the commissioning process via QR code or manual pairing code +* **Network Connectivity**: Checking and managing Wi-Fi and Thread connections +* **Event Handling**: Monitoring Matter events and device state changes +* **Device Management**: Decommissioning and factory reset functionality + +The ``Matter`` class is implemented as a singleton, meaning there's only one instance available globally. You access it directly as ``Matter`` without creating an instance. + +The ``Matter`` class provides the following key methods: + +* ``begin()``: Initializes the Matter stack +* ``isDeviceCommissioned()``: Checks if the device is commissioned +* ``isWi-FiConnected()``: Checks Wi-Fi connection status (if Wi-Fi is enabled) +* ``isThreadConnected()``: Checks Thread connection status (if Thread is enabled) +* ``isDeviceConnected()``: Checks overall device connectivity +* ``decommission()``: Factory resets the device +* ``getManualPairingCode()``: Gets the manual pairing code for commissioning +* ``getOnboardingQRCodeUrl()``: Gets the QR code URL for commissioning +* ``onEvent()``: Sets a callback for Matter events + +MatterEndPoint +************** + +The ``MatterEndPoint`` class is the base class for all Matter endpoints. It provides common functionality for all endpoint types. + +* **Endpoint Management**: Each endpoint has a unique endpoint ID for identification +* **Attribute Access**: Methods to get and set attribute values from Matter clusters +* **Identify Cluster**: Support for device identification (visual feedback) +* **Secondary Network Interfaces**: Support for multiple network interfaces (Wi-Fi, Thread, Ethernet) +* **Attribute Change Callbacks**: Base framework for handling attribute changes from Matter controllers + +.. toctree:: + :maxdepth: 2 + + matter_ep + +Specific endpoint classes +************************* + +The library provides specialized endpoint classes for different device types. Each endpoint type includes specific clusters and functionality relevant to that device category. + +**Lighting Endpoints:** + +* ``MatterOnOffLight``: Simple on/off light control +* ``MatterDimmableLight``: Light with brightness control +* ``MatterColorTemperatureLight``: Light with color temperature control +* ``MatterColorLight``: Light with RGB color control (HSV color model) +* ``MatterEnhancedColorLight``: Enhanced color light with color temperature and brightness control + +**Sensor Endpoints:** + +* ``MatterTemperatureSensor``: Temperature sensor (read-only) +* ``MatterHumiditySensor``: Humidity sensor (read-only) +* ``MatterPressureSensor``: Pressure sensor (read-only) +* ``MatterContactSensor``: Contact sensor (open/closed state) +* ``MatterWaterLeakDetector``: Water leak detector (detected/not detected state) +* ``MatterWaterFreezeDetector``: Water freeze detector (detected/not detected state) +* ``MatterRainSensor``: Rain sensor (detected/not detected state) +* ``MatterOccupancySensor``: Occupancy sensor (occupied/unoccupied state) + +**Control Endpoints:** + +* ``MatterTemperatureControlledCabinet``: Temperature controlled cabinet (setpoint control with min/max limits) + +* ``MatterFan``: Fan with speed and mode control +* ``MatterThermostat``: Thermostat with temperature control and setpoints +* ``MatterOnOffPlugin``: On/off plugin unit (power outlet/relay) +* ``MatterDimmablePlugin``: Dimmable plugin unit (power outlet/relay with brightness control) +* ``MatterGenericSwitch``: Generic switch endpoint (smart button) + +.. toctree:: + :maxdepth: 1 + :glob: + + ep_* + +Common Problems and Issues +-------------------------- + +Troubleshooting +--------------- + +Common Issues +************* + +**Device won't commission** + * Ensure the Matter hub is in pairing mode + * Check that Wi-Fi or Thread connectivity is properly configured + * Verify the QR code or pairing code is correct + * For ESP32/ESP32-S2, ensure Wi-Fi credentials are set in the code + +**Commissioning fails** + * Try factory resetting the device by calling ``Matter.decommission()`` + * Erase flash memory: ``Arduino IDE Menu`` -> ``Tools`` -> ``Erase All Flash Before Sketch Upload: "Enabled"`` + * Or use esptool: ``esptool.py --port erase_flash`` + +**Wi-Fi connection issues** + * Verify Wi-Fi credentials (SSID and password) are correct + * Check that the device is within range of the Wi-Fi router + * Ensure the Wi-Fi network is 2.4 GHz (Matter requires 2.4 GHz Wi-Fi) + +**Thread connection issues** + * Verify Thread border router is properly configured + * Check that Thread network credentials are correct + * Ensure device supports Thread (ESP32-H2, ESP32-C6 with Thread enabled) + +**Device not responding** + * Check Serial Monitor for error messages (115200 baud) + * Verify endpoint initialization with ``begin()`` method + * Ensure ``Matter.begin()`` is called after all endpoints are initialized + +**Callbacks not firing** + * Verify callback functions are registered before commissioning + * Check that callback functions are properly defined + * Ensure endpoint is properly initialized with ``begin()`` + +Factory Reset +************* + +If you have problems with commissioning or device connectivity, you can try to factory reset the device. This will erase all the Matter network settings and act as a brand new device. + +.. code-block:: arduino + + Matter.decommission(); + +This will reset the device and it will need to be commissioned again. + +Event Monitoring +**************** + +For debugging and monitoring Matter events, you can set up an event callback: + +.. code-block:: arduino + + Matter.onEvent([](matterEvent_t event, const chip::DeviceLayer::ChipDeviceEvent *eventData) { + Serial.printf("Matter Event: 0x%04X\r\n", event); + // Handle specific events + switch(event) { + case MATTER_COMMISSIONING_COMPLETE: + Serial.println("Device commissioned!"); + break; + case MATTER_WIFI_CONNECTIVITY_CHANGE: + Serial.println("Wi-Fi connectivity changed"); + break; + // ... handle other events + } + }); + +This allows you to monitor commissioning progress, connectivity changes, and other Matter events in real-time. diff --git a/docs/en/matter/matter_ep.rst b/docs/en/matter/matter_ep.rst new file mode 100644 index 00000000000..084d22e2125 --- /dev/null +++ b/docs/en/matter/matter_ep.rst @@ -0,0 +1,200 @@ +############## +MatterEndPoint +############## + +About +----- + +The ``MatterEndPoint`` class is the base class for all Matter endpoints. It provides common functionality for all endpoint types. + +* **Endpoint Management**: Each endpoint has a unique endpoint ID for identification within the Matter network +* **Attribute Access**: Methods to get and set attribute values from Matter clusters +* **Identify Cluster**: Support for device identification (visual feedback like LED blinking) +* **Secondary Network Interfaces**: Support for multiple network interfaces (Wi-Fi, Thread, Ethernet) +* **Attribute Change Callbacks**: Base framework for handling attribute changes from Matter controllers + +All Matter endpoint classes inherit from ``MatterEndPoint``, providing a consistent interface and common functionality across all device types. + +MatterEndPoint APIs +------------------- + +Endpoint Management +******************* + +getEndPointId +^^^^^^^^^^^^^ + +Gets the current Matter Accessory endpoint ID. + +.. code-block:: arduino + + uint16_t getEndPointId(); + +This function will return the endpoint number (typically 1-254). + +setEndPointId +^^^^^^^^^^^^^ + +Sets the current Matter Accessory endpoint ID. + +.. code-block:: arduino + + void setEndPointId(uint16_t ep); + +* ``ep`` - Endpoint number to set + +Secondary Network Interface +*************************** + +createSecondaryNetworkInterface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Creates a secondary network interface endpoint. This can be used for devices that support multiple network interfaces, such as Ethernet, Thread and Wi-Fi. + +.. code-block:: arduino + + bool createSecondaryNetworkInterface(); + +This function will return ``true`` if successful, ``false`` otherwise. + +getSecondaryNetworkEndPointId +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the secondary network interface endpoint ID. + +.. code-block:: arduino + + uint16_t getSecondaryNetworkEndPointId(); + +This function will return the secondary network endpoint ID, or 0 if not created. + +Attribute Management +******************** + +getAttribute +^^^^^^^^^^^^ + +Gets a pointer to an attribute from its cluster ID and attribute ID. + +.. code-block:: arduino + + esp_matter::attribute_t *getAttribute(uint32_t cluster_id, uint32_t attribute_id); + +* ``cluster_id`` - Cluster ID (e.g., ``OnOff::Attributes::OnOff::Id``) +* ``attribute_id`` - Attribute ID (e.g., ``OnOff::Attributes::OnOff::Id``) + +This function will return a pointer to the attribute, or ``NULL`` if not found. + +getAttributeVal +^^^^^^^^^^^^^^^ + +Gets the value of an attribute from its cluster ID and attribute ID. + +.. code-block:: arduino + + bool getAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal); + +* ``cluster_id`` - Cluster ID +* ``attribute_id`` - Attribute ID +* ``attrVal`` - Pointer to store the attribute value + +This function will return ``true`` if successful, ``false`` otherwise. + +setAttributeVal +^^^^^^^^^^^^^^^ + +Sets the value of an attribute from its cluster ID and attribute ID. + +.. code-block:: arduino + + bool setAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal); + +* ``cluster_id`` - Cluster ID +* ``attribute_id`` - Attribute ID +* ``attrVal`` - Pointer to the attribute value to set + +This function will return ``true`` if successful, ``false`` otherwise. + +updateAttributeVal +^^^^^^^^^^^^^^^^^^ + +Updates the value of an attribute from its cluster ID. This is typically used for read-only attributes that are updated by the device itself (e.g., sensor readings). + +.. code-block:: arduino + + bool updateAttributeVal(uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *attrVal); + +* ``cluster_id`` - Cluster ID +* ``attribute_id`` - Attribute ID +* ``attrVal`` - Pointer to the attribute value to update + +This function will return ``true`` if successful, ``false`` otherwise. + +Identify Cluster +**************** + +onIdentify +^^^^^^^^^^ + +Sets a callback function for the Identify cluster. This callback is invoked when clients interact with the Identify Cluster of a specific endpoint. + +.. code-block:: arduino + + void onIdentify(EndPointIdentifyCB onEndPointIdentifyCB); + +* ``onEndPointIdentifyCB`` - Function pointer to the callback function. The callback receives a boolean parameter indicating if identify is enabled. + +The callback signature is: + +.. code-block:: arduino + + bool identifyCallback(bool identifyIsEnabled); + +When ``identifyIsEnabled`` is ``true``, the device should provide visual feedback (e.g., blink an LED). When ``false``, the device should stop the identification feedback. + +Example usage: + +.. code-block:: arduino + + myEndpoint.onIdentify([](bool identifyIsEnabled) { + if (identifyIsEnabled) { + // Start blinking LED + digitalWrite(LED_PIN, HIGH); + } else { + // Stop blinking LED + digitalWrite(LED_PIN, LOW); + } + return true; + }); + +Attribute Change Callback +************************* + +attributeChangeCB +^^^^^^^^^^^^^^^^^ + +This function is called by the Matter internal event processor when an attribute changes. It can be overridden by the application if necessary. + +.. code-block:: arduino + + virtual bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +* ``endpoint_id`` - Endpoint ID where the attribute changed +* ``cluster_id`` - Cluster ID of the changed attribute +* ``attribute_id`` - Attribute ID that changed +* ``val`` - Pointer to the new attribute value + +This function should return ``true`` if the change was handled successfully, ``false`` otherwise. + +All endpoint classes implement this function to handle attribute changes specific to their device type. You typically don't need to override this unless you need custom behavior. + +Supported Endpoints +------------------- + +The Matter library provides specialized endpoint classes that inherit from ``MatterEndPoint``. Each endpoint type includes specific clusters and functionality relevant to that device category. + +.. toctree:: + :maxdepth: 1 + :glob: + + ep_* diff --git a/docs/en/openthread/openthread.rst b/docs/en/openthread/openthread.rst new file mode 100644 index 00000000000..9a72d035b2e --- /dev/null +++ b/docs/en/openthread/openthread.rst @@ -0,0 +1,296 @@ +########## +OpenThread +########## + +About +----- + +The OpenThread library provides support for creating Thread network devices using ESP32 SoCs with IEEE 802.15.4 radio support. The library offers two different programming interfaces for interacting with the OpenThread stack: + +* **Stream-based CLI enhanced with Helper Functions API**: Command-line interface helper functions that send OpenThread CLI commands and parse responses +* **Classes API**: Object-oriented classes that directly call OpenThread API functions + +The OpenThread library is built on top of `ESP OpenThread `_ and provides a high-level Arduino-style interface for creating Thread devices. + +Thread Protocol Overview +************************ + +Thread is an IPv6-based, low-power wireless mesh networking protocol designed for smart home and IoT applications. It provides secure, reliable, and scalable connectivity for battery-powered devices. + +**Key Features:** + +* **IPv6-based**: Native IPv6 addressing and routing +* **Mesh Networking**: Self-healing mesh topology with automatic routing +* **Low Power**: Optimized for battery-operated devices +* **Security**: Built-in security features including encryption and authentication +* **Scalability**: Supports up to 250+ devices per network +* **Reliability**: Automatic route discovery and self-healing capabilities + +Thread Network Topology +*********************** + +.. code-block:: text + + ┌─────────────────┐ + │ Internet │ + └─────────────────┘ + ▲ + │ + │ + ┌─────────────────┐ + │ Wi-Fi Router │ + │ │ + └─────────────────┘ + │ + ┌───────────────┴───────────────┐ + │ │ + ▼ ▼ + ┌───────────────────────┐ ┌──────────────────┐ + │ Other Wi-Fi Devices │ │ Thread Border │ + │ │ │ Router │ + └───────────────────────┘ └──────────────────┘ + │ + │ Thread Network + │ + ┌─────────────────────────┼─────────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ Thread Leader │◄─────►│ Thread Router │◄─────►│ Thread Child │ + │ │ │ │ │ │ + └─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + └──────────────────────────┴──────────────────────────┘ + Other Thread Nodes + + +**Thread Device Roles:** + +* **Leader**: Manages the Thread network, assigns router IDs, and maintains network state. This device should be powered by a wall adapter. +* **Router**: Extends network range, routes messages, maintains network topology. This device should be powered by a wall adapter. +* **Child**: End device that can sleep for extended periods (battery-powered devices). It can be powered by a battery or a wall adapter. +* **Detached**: Device not currently participating in a Thread network. +* **Disabled**: The Thread stack and interface are disabled. + +**Other Thread Network Devices:** + +* **Thread Border Router**: A device that connects a Thread network to other IP-based networks in the external world. The Thread Border Router is connected to both the Thread network and external IP networks (like a Wi-Fi router), enabling Thread devices to communicate with devices on other networks and the Internet. A Border Router can be implemented on a device with any Thread device role (Leader, Router, or Child). It can also act as a gateway to other protocols such as MQTT or Zigbee. + +OpenThread Library Structure +---------------------------- + +**The library provides two main programming interfaces:** + +* **CLI Helper Functions API**: Functions that interact with OpenThread through the CLI interface + * ``otGetRespCmd()``: Execute CLI command and get response + * ``otExecCommand()``: Execute CLI command with arguments + * ``otPrintRespCLI()``: Execute CLI command and print response to Stream + * ``OpenThreadCLI``: Stream-based CLI interface class + +* **Classes API**: Object-oriented classes that directly call OpenThread API functions + * ``OpenThread``: Main class for managing Thread network operations + * ``DataSet``: Class for managing Thread operational datasets + +OpenThread Class +**************** + +The ``OpenThread`` class is the main entry point for Thread operations using the Classes API. It provides direct access to OpenThread API functions for managing the Thread network. + +* **Network Management**: Starting, stopping, and managing the Thread network +* **Device Role Management**: Getting and monitoring device role (Leader, Router, Child, Detached, Disabled) +* **Dataset Management**: Setting and getting operational dataset parameters +* **Address Management**: Getting mesh-local addresses, RLOC, and multicast addresses +* **Network Information**: Getting network name, channel, PAN ID, and other network parameters + +The ``OpenThread`` class is implemented as a singleton, meaning there's only one instance available globally. You access it directly as ``OThread`` without creating an instance. + +.. toctree:: + :maxdepth: 2 + + openthread_core + +DataSet Class +************* + +The ``DataSet`` class provides a convenient way to manage Thread operational datasets. It allows you to create, configure, and apply operational datasets to the Thread network. + +* **Dataset Creation**: Create new operational datasets +* **Parameter Configuration**: Set network name, channel, PAN ID, network key, and extended PAN ID +* **Dataset Application**: Apply datasets to the Thread network +* **Dataset Retrieval**: Get current dataset parameters + +.. toctree:: + :maxdepth: 2 + + openthread_dataset + +OpenThreadCLI +************* + +The ``OpenThreadCLI`` class provides a Stream-based interface for interacting with the OpenThread CLI. It allows you to send CLI commands and receive responses programmatically. + +* **CLI Interface**: Stream-based interface for sending commands and receiving responses +* **Command Execution**: Execute OpenThread CLI commands programmatically +* **Response Handling**: Parse and handle CLI command responses +* **Console Mode**: Interactive console mode for debugging and testing + +.. toctree:: + :maxdepth: 2 + + openthread_cli + +CLI Helper Functions API +************************* + +The CLI Helper Functions API provides utility functions for executing OpenThread CLI commands and parsing responses. This API is useful when you need to interact with OpenThread through the CLI interface. + +* **Command Execution**: Execute CLI commands with arguments +* **Response Parsing**: Get and parse CLI command responses +* **Error Handling**: Handle CLI command errors and return codes + +**Key Functions:** + +* ``otGetRespCmd()``: Execute CLI command and get response string +* ``otExecCommand()``: Execute CLI command with arguments and error handling +* ``otPrintRespCLI()``: Execute CLI command and print response to Stream + +For detailed documentation on the CLI Helper Functions API, see the :doc:`openthread_cli` documentation. + +Supported Hardware +------------------ + +The OpenThread library requires ESP32 SoCs with IEEE 802.15.4 radio support: + +* **ESP32-H2**: Native Thread support with IEEE 802.15.4 radio +* **ESP32-C6**: Thread support with IEEE 802.15.4 radio (when Thread is enabled) +* **ESP32-C5**: Thread support with IEEE 802.15.4 radio (when Thread is enabled) + +**Note:** Thread support must be enabled in the ESP-IDF configuration (``CONFIG_OPENTHREAD_ENABLED``). This is done automatically when using the ESP32 Arduino OpenThread library. + +Common Problems and Issues +-------------------------- + +Troubleshooting +--------------- + +Common Issues +************* + +**Thread network not starting** + * Ensure the device has IEEE 802.15.4 radio support (ESP32-H2, ESP32-C6, ESP32-C5) + * Check that Thread is enabled in ESP-IDF configuration (``CONFIG_OPENTHREAD_ENABLED``) + * Verify that ``OpenThread::begin()`` is called before using Thread functions + * Check Serial Monitor for initialization errors + +**Device not joining network** + * Verify the operational dataset parameters (network name, network key, channel, PAN ID) + * Ensure the device is within range of the Thread network + * Check that the network key and extended PAN ID match the network you're trying to join + * Verify the device role is not "Detached" + +**CLI commands not working** + * Ensure ``OpenThreadCLI::begin()`` is called before using CLI functions + * Check that ``OpenThreadCLI::startConsole()`` is called with a valid Stream + * Verify the CLI is properly initialized and running + * Check Serial Monitor for CLI initialization errors + +**Address not available** + * Wait for the device to join the Thread network (role should be Child, Router, or Leader) + * Check that the network interface is up using ``networkInterfaceUp()`` + * Verify the device has obtained a mesh-local address + * Check network connectivity using ``getRloc()`` or ``getMeshLocalEid()`` + +**Dataset not applying** + * Ensure all required dataset parameters are set (network name, network key, channel) + * Verify the dataset is valid before applying + * Check that OpenThread is started before applying the dataset + * Verify the dataset parameters match the target network + +Initialization Order +******************** + +For proper initialization, follow this order: + +1. Initialize OpenThread stack: ``OpenThread::begin()`` +2. Initialize OpenThreadCLI (if using CLI): ``OpenThreadCLI::begin()`` +3. Start CLI console (if using CLI): ``OpenThreadCLI::startConsole()`` +4. Configure dataset (if needed): Create and configure ``DataSet`` +5. Apply dataset (if needed): ``OThread.commitDataSet(dataset)`` +6. Start Thread network: ``OThread.start()`` +7. Bring network interface up: ``OThread.networkInterfaceUp()`` + +Example +------- + +Basic OpenThread Setup +********************** + +Using the Classes API: + +.. code-block:: arduino + + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread stack + OpenThread::begin(); + + // Wait for OpenThread to be ready + while (!OThread) { + delay(100); + } + + // Create and configure dataset + DataSet dataset; + dataset.initNew(); + dataset.setNetworkName("MyThreadNetwork"); + dataset.setChannel(15); + + // Apply dataset and start network + OThread.commitDataSet(dataset); + OThread.start(); + OThread.networkInterfaceUp(); + + // Print network information + OpenThread::otPrintNetworkInformation(Serial); + } + +Using the CLI Helper Functions API: + +.. code-block:: arduino + + #include + #include + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread stack + OpenThread::begin(); + + // Initialize and start CLI + OThreadCLI.begin(); + OThreadCLI.startConsole(Serial); + + // Wait for CLI to be ready + while (!OThreadCLI) { + delay(100); + } + + // Execute CLI commands + char resp[256]; + if (otGetRespCmd("state", resp)) { + Serial.printf("Thread state: %s\r\n", resp); + } + + if (otExecCommand("networkname", "MyThreadNetwork", NULL)) { + Serial.println("Network name set successfully"); + } + + if (otExecCommand("ifconfig", "up", NULL)) { + Serial.println("Network interface up"); + } + } diff --git a/docs/en/openthread/openthread_cli.rst b/docs/en/openthread/openthread_cli.rst new file mode 100644 index 00000000000..1d598fb5063 --- /dev/null +++ b/docs/en/openthread/openthread_cli.rst @@ -0,0 +1,580 @@ +############## +OpenThread CLI +############## + +About +----- + +The OpenThread CLI (Command-Line Interface) provides two ways to interact with the OpenThread stack through CLI commands: + +* **CLI Helper Functions API**: Utility functions that execute CLI commands and parse responses +* **OpenThreadCLI Class**: Stream-based interface for interactive CLI access + +The CLI Helper Functions API is useful for programmatic control using OpenThread CLI commands, while the ``OpenThreadCLI`` class provides a Stream interface for interactive console access. + +CLI Helper Functions API +************************ + +The CLI Helper Functions API consists of utility functions that execute OpenThread CLI commands and handle responses. These functions interact with the OpenThread CLI through the ``OpenThreadCLI`` interface. + +otGetRespCmd +^^^^^^^^^^^^ + +Executes a CLI command and gets the response. + +.. code-block:: arduino + + bool otGetRespCmd(const char *cmd, char *resp = NULL, uint32_t respTimeout = 5000); + +* ``cmd`` - The CLI command to execute (e.g., ``"state"``, ``"networkname"``) +* ``resp`` - Buffer to store the response (optional, can be ``NULL``) +* ``respTimeout`` - Timeout in milliseconds for waiting for response (default: 5000 ms) + +This function executes a CLI command and collects all response lines until "Done" or "Error" is received. If ``resp`` is not ``NULL``, the response is stored in the buffer. + +**Returns:** ``true`` if command executed successfully, ``false`` on error or timeout. + +**Example:** + +.. code-block:: arduino + + char response[256]; + if (otGetRespCmd("state", response)) { + Serial.printf("Thread state: %s\r\n", response); + } + + if (otGetRespCmd("networkname", response)) { + Serial.printf("Network name: %s\r\n", response); + } + +otExecCommand +^^^^^^^^^^^^^ + +Executes a CLI command with arguments. + +.. code-block:: arduino + + bool otExecCommand(const char *cmd, const char *arg, ot_cmd_return_t *returnCode = NULL); + +* ``cmd`` - The CLI command to execute (e.g., ``"networkname"``, ``"channel"``) +* ``arg`` - The command argument (can be ``NULL`` for commands without arguments) +* ``returnCode`` - Pointer to ``ot_cmd_return_t`` structure to receive error information (optional) + +This function executes a CLI command with an optional argument and returns the success status. If ``returnCode`` is provided, it will be populated with error information on failure. + +**Returns:** ``true`` if command executed successfully, ``false`` on error. + +**Error Structure:** + +.. code-block:: arduino + + typedef struct { + int errorCode; // OpenThread error code + String errorMessage; // Error message string + } ot_cmd_return_t; + +**Example:** + +.. code-block:: arduino + + ot_cmd_return_t errorInfo; + + // Set network name + if (otExecCommand("networkname", "MyThreadNetwork", &errorInfo)) { + Serial.println("Network name set successfully"); + } else { + Serial.printf("Error %d: %s\r\n", errorInfo.errorCode, errorInfo.errorMessage.c_str()); + } + + // Set channel + if (otExecCommand("channel", "15", &errorInfo)) { + Serial.println("Channel set successfully"); + } + + // Bring interface up + if (otExecCommand("ifconfig", "up", NULL)) { + Serial.println("Interface is up"); + } + +otPrintRespCLI +^^^^^^^^^^^^^^ + +Executes a CLI command and prints the response to a Stream. + +.. code-block:: arduino + + bool otPrintRespCLI(const char *cmd, Stream &output, uint32_t respTimeout); + +* ``cmd`` - The CLI command to execute +* ``output`` - The Stream object to print responses to (e.g., ``Serial``) +* ``respTimeout`` - Timeout in milliseconds per response line (default: 5000 ms) + +This function executes a CLI command and prints all response lines to the specified Stream until "Done" or "Error" is received. + +**Returns:** ``true`` if command executed successfully, ``false`` on error or timeout. + +**Example:** + +.. code-block:: arduino + + // Print all IP addresses + if (otPrintRespCLI("ipaddr", Serial, 5000)) { + Serial.println("IP addresses printed"); + } + + // Print all multicast addresses + if (otPrintRespCLI("ipmaddr", Serial, 5000)) { + Serial.println("Multicast addresses printed"); + } + +OpenThreadCLI Class +******************* + +The ``OpenThreadCLI`` class provides a Stream-based interface for interacting with the OpenThread CLI. It allows you to send CLI commands and receive responses programmatically or through an interactive console. + +Initialization +************** + +begin +^^^^^ + +Initializes the OpenThread CLI. + +.. code-block:: arduino + + void begin(); + +This function initializes the OpenThread CLI interface. It must be called after ``OpenThread::begin()`` and before using any CLI functions. + +**Note:** The OpenThread stack must be started before initializing the CLI. + +end +^^^ + +Stops and cleans up the OpenThread CLI. + +.. code-block:: arduino + + void end(); + +This function stops the CLI interface and cleans up all CLI resources. + +Console Management +****************** + +startConsole +^^^^^^^^^^^^ + +Starts an interactive console for CLI access. + +.. code-block:: arduino + + void startConsole(Stream &otStream, bool echoback = true, const char *prompt = "ot> "); + +* ``otStream`` - The Stream object for console I/O (e.g., ``Serial``) +* ``echoback`` - If ``true``, echo characters back to the console (default: ``true``) +* ``prompt`` - The console prompt string (default: ``"ot> "``, can be ``NULL`` for no prompt) + +This function starts an interactive console task that allows you to type CLI commands directly. The console will echo input and display responses. + +**Example:** + +.. code-block:: arduino + + OThreadCLI.startConsole(Serial, true, "ot> "); + +stopConsole +^^^^^^^^^^^ + +Stops the interactive console. + +.. code-block:: arduino + + void stopConsole(); + +This function stops the interactive console task. + +setStream +^^^^^^^^^ + +Changes the console Stream object. + +.. code-block:: arduino + + void setStream(Stream &otStream); + +* ``otStream`` - The new Stream object for console I/O + +This function changes the Stream object used by the console. + +setEchoBack +^^^^^^^^^^^ + +Changes the echo back setting. + +.. code-block:: arduino + + void setEchoBack(bool echoback); + +* ``echoback`` - If ``true``, echo characters back to the console + +This function changes whether characters are echoed back to the console. + +setPrompt +^^^^^^^^^ + +Changes the console prompt. + +.. code-block:: arduino + + void setPrompt(char *prompt); + +* ``prompt`` - The new prompt string (can be ``NULL`` for no prompt) + +This function changes the console prompt string. + +onReceive +^^^^^^^^^ + +Sets a callback function for CLI responses. + +.. code-block:: arduino + + void onReceive(OnReceiveCb_t func); + +* ``func`` - Callback function to call when a complete line of output is received + +The callback function is called whenever a complete line of output is received from the OpenThread CLI. This allows you to process CLI responses asynchronously. + +**Callback Signature:** + +.. code-block:: arduino + + typedef std::function OnReceiveCb_t; + +**Example:** + +.. code-block:: arduino + + void handleCLIResponse() { + while (OThreadCLI.available() > 0) { + char c = OThreadCLI.read(); + // Process response character + } + } + + OThreadCLI.onReceive(handleCLIResponse); + +Buffer Management +***************** + +setTxBufferSize +^^^^^^^^^^^^^^^ + +Sets the transmit buffer size. + +.. code-block:: arduino + + size_t setTxBufferSize(size_t tx_queue_len); + +* ``tx_queue_len`` - The size of the transmit buffer in bytes (default: 256) + +This function sets the size of the transmit buffer used for sending CLI commands. + +**Returns:** The actual buffer size set, or 0 on error. + +setRxBufferSize +^^^^^^^^^^^^^^^ + +Sets the receive buffer size. + +.. code-block:: arduino + + size_t setRxBufferSize(size_t rx_queue_len); + +* ``rx_queue_len`` - The size of the receive buffer in bytes (default: 1024) + +This function sets the size of the receive buffer used for receiving CLI responses. + +**Returns:** The actual buffer size set, or 0 on error. + +Stream Interface +**************** + +The ``OpenThreadCLI`` class implements the Arduino ``Stream`` interface, allowing you to use it like any other Stream object. + +write +^^^^^ + +Writes a byte to the CLI. + +.. code-block:: arduino + + size_t write(uint8_t c); + +* ``c`` - The byte to write + +This function writes a single byte to the CLI transmit buffer. + +**Returns:** The number of bytes written (1 on success, 0 on failure). + +available +^^^^^^^^^ + +Checks if data is available to read. + +.. code-block:: arduino + + int available(); + +This function returns the number of bytes available in the receive buffer. + +**Returns:** Number of bytes available, or -1 if CLI is not initialized. + +read +^^^^ + +Reads a byte from the CLI. + +.. code-block:: arduino + + int read(); + +This function reads a single byte from the CLI receive buffer. + +**Returns:** The byte read, or -1 if no data is available. + +peek +^^^^ + +Peeks at the next byte without removing it. + +.. code-block:: arduino + + int peek(); + +This function returns the next byte in the receive buffer without removing it. + +**Returns:** The byte, or -1 if no data is available. + +flush +^^^^^ + +Flushes the transmit buffer. + +.. code-block:: arduino + + void flush(); + +This function waits for all data in the transmit buffer to be sent. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns whether the CLI is started. + +.. code-block:: arduino + + operator bool() const; + +This operator returns ``true`` if the CLI is started and ready, ``false`` otherwise. + +**Example:** + +.. code-block:: arduino + + if (OThreadCLI) { + Serial.println("CLI is ready"); + } + +Example +------- + +Using CLI Helper Functions API +****************************** + +.. code-block:: arduino + + #include + #include + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Initialize CLI + OThreadCLI.begin(); + while (!OThreadCLI) { + delay(100); + } + + // Get network state + char resp[256]; + if (otGetRespCmd("state", resp)) { + Serial.printf("Thread state: %s\r\n", resp); + } + + // Set network name + ot_cmd_return_t errorInfo; + if (otExecCommand("networkname", "MyThreadNetwork", &errorInfo)) { + Serial.println("Network name set"); + } else { + Serial.printf("Error: %s\r\n", errorInfo.errorMessage.c_str()); + } + + // Set channel + if (otExecCommand("channel", "15", NULL)) { + Serial.println("Channel set"); + } + + // Bring interface up + if (otExecCommand("ifconfig", "up", NULL)) { + Serial.println("Interface up"); + } + + // Print IP addresses + otPrintRespCLI("ipaddr", Serial, 5000); + } + +Using OpenThreadCLI Class +************************* + +Interactive Console +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #include + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Initialize and start CLI console + OThreadCLI.begin(); + OThreadCLI.startConsole(Serial, true, "ot> "); + + Serial.println("OpenThread CLI Console Ready"); + Serial.println("Type OpenThread CLI commands (e.g., 'state', 'networkname')"); + } + +Programmatic CLI Access +^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #include + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Initialize CLI + OThreadCLI.begin(); + while (!OThreadCLI) { + delay(100); + } + + // Send CLI commands programmatically + OThreadCLI.println("state"); + delay(100); + while (OThreadCLI.available() > 0) { + char c = OThreadCLI.read(); + Serial.write(c); + } + + // Send command with argument + OThreadCLI.print("networkname "); + OThreadCLI.println("MyThreadNetwork"); + delay(100); + while (OThreadCLI.available() > 0) { + char c = OThreadCLI.read(); + Serial.write(c); + } + } + +Using Callback for Responses +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #include + #include + + void handleCLIResponse() { + String response = ""; + while (OThreadCLI.available() > 0) { + char c = OThreadCLI.read(); + if (c == '\n' || c == '\r') { + if (response.length() > 0) { + Serial.printf("CLI Response: %s\r\n", response.c_str()); + response = ""; + } + } else { + response += c; + } + } + } + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Initialize CLI with callback + OThreadCLI.begin(); + OThreadCLI.onReceive(handleCLIResponse); + + // Send commands + OThreadCLI.println("state"); + delay(500); + OThreadCLI.println("networkname"); + delay(500); + } + +Common OpenThread CLI Commands +****************************** + +Here are some commonly used OpenThread CLI commands: + +* ``state`` - Get the current Thread state +* ``networkname `` - Set or get the network name +* ``channel `` - Set or get the channel (11-26) +* ``panid `` - Set or get the PAN ID +* ``extpanid `` - Set or get the extended PAN ID +* ``networkkey `` - Set or get the network key +* ``ifconfig up`` - Bring the network interface up +* ``ifconfig down`` - Bring the network interface down +* ``ipaddr`` - List all IPv6 addresses +* ``ipmaddr`` - List all multicast addresses +* ``rloc16`` - Get the RLOC16 +* ``leaderdata`` - Get leader data +* ``router table`` - Get router table +* ``child table`` - Get child table + +For a complete list of OpenThread CLI commands, refer to the `OpenThread CLI Reference `_. diff --git a/docs/en/openthread/openthread_core.rst b/docs/en/openthread/openthread_core.rst new file mode 100644 index 00000000000..0ba88acde88 --- /dev/null +++ b/docs/en/openthread/openthread_core.rst @@ -0,0 +1,579 @@ +################ +OpenThread Class +################ + +About +----- + +The ``OpenThread`` class provides direct access to OpenThread API functions for managing Thread network operations. This is the **Classes API** approach, which offers object-oriented methods that directly call OpenThread API functions. + +**Key Features:** +* Direct OpenThread API access +* Network management (start, stop, interface control) +* Dataset management +* Address management with caching +* Network information retrieval +* Device role monitoring + +**Use Cases:** +* Thread network configuration and management +* Direct control over Thread operations +* Programmatic network setup +* Address and routing information access + +API Reference +------------- + +Initialization +************** + +begin +^^^^^ + +Initializes the OpenThread stack. + +.. code-block:: arduino + + static void begin(bool OThreadAutoStart = true); + +* ``OThreadAutoStart`` - If ``true``, automatically starts Thread with default dataset from NVS or ESP-IDF settings (default: ``true``) + +This function initializes the OpenThread stack and creates the OpenThread task. If ``OThreadAutoStart`` is ``true``, it will attempt to start Thread using the active dataset from NVS or ESP-IDF default settings. + +**Note:** This is a static function and should be called before creating an ``OpenThread`` instance. + +end +^^^ + +Stops and cleans up the OpenThread stack. + +.. code-block:: arduino + + static void end(); + +This function stops the OpenThread task and cleans up all OpenThread resources. It should be called when you no longer need the OpenThread stack. + +**Note:** This is a static function. + +Network Control +*************** + +start +^^^^^ + +Starts the Thread network. + +.. code-block:: arduino + + void start(); + +This function enables the Thread network. The device will attempt to join or form a Thread network based on the active dataset. + +**Note:** The network interface must be brought up separately using ``networkInterfaceUp()``. + +stop +^^^^ + +Stops the Thread network. + +.. code-block:: arduino + + void stop(); + +This function disables the Thread network. The device will leave the Thread network and stop participating in Thread operations. + +networkInterfaceUp +^^^^^^^^^^^^^^^^^^ + +Brings the Thread network interface up. + +.. code-block:: arduino + + void networkInterfaceUp(); + +This function enables the Thread IPv6 interface (equivalent to CLI command ``ifconfig up``). The device will be able to send and receive IPv6 packets over the Thread network. + +networkInterfaceDown +^^^^^^^^^^^^^^^^^^^^ + +Brings the Thread network interface down. + +.. code-block:: arduino + + void networkInterfaceDown(); + +This function disables the Thread IPv6 interface (equivalent to CLI command ``ifconfig down``). The device will stop sending and receiving IPv6 packets. + +Dataset Management +****************** + +commitDataSet +^^^^^^^^^^^^^ + +Commits an operational dataset to the Thread network. + +.. code-block:: arduino + + void commitDataSet(const DataSet &dataset); + +* ``dataset`` - The ``DataSet`` object containing the operational dataset parameters + +This function sets the active operational dataset for the Thread network. The dataset must be properly configured before committing. + +**Example:** + +.. code-block:: arduino + + DataSet dataset; + dataset.initNew(); + dataset.setNetworkName("MyThreadNetwork"); + dataset.setChannel(15); + dataset.setNetworkKey(networkKey); + OThread.commitDataSet(dataset); + +getCurrentDataSet +^^^^^^^^^^^^^^^^^ + +Gets the current active operational dataset. + +.. code-block:: arduino + + const DataSet &getCurrentDataSet() const; + +This function returns a reference to a ``DataSet`` object containing the current active operational dataset parameters. + +Network Information +******************* + +getNetworkName +^^^^^^^^^^^^^^ + +Gets the Thread network name. + +.. code-block:: arduino + + String getNetworkName() const; + +This function returns the network name as a ``String``. + +getExtendedPanId +^^^^^^^^^^^^^^^^ + +Gets the extended PAN ID. + +.. code-block:: arduino + + const uint8_t *getExtendedPanId() const; + +This function returns a pointer to an 8-byte array containing the extended PAN ID. + +getNetworkKey +^^^^^^^^^^^^^ + +Gets the network key. + +.. code-block:: arduino + + const uint8_t *getNetworkKey() const; + +This function returns a pointer to a 16-byte array containing the network key. + +**Note:** The network key is stored in static storage and persists after the function returns. + +getChannel +^^^^^^^^^^ + +Gets the Thread channel. + +.. code-block:: arduino + + uint8_t getChannel() const; + +This function returns the Thread channel number (11-26). + +getPanId +^^^^^^^^ + +Gets the PAN ID. + +.. code-block:: arduino + + uint16_t getPanId() const; + +This function returns the PAN ID as a 16-bit value. + +Device Role +*********** + +otGetDeviceRole +^^^^^^^^^^^^^^^ + +Gets the current device role. + +.. code-block:: arduino + + static ot_device_role_t otGetDeviceRole(); + +This function returns the current Thread device role: + +* ``OT_ROLE_DISABLED`` - The Thread stack is disabled +* ``OT_ROLE_DETACHED`` - Not currently participating in a Thread network +* ``OT_ROLE_CHILD`` - The Thread Child role +* ``OT_ROLE_ROUTER`` - The Thread Router role +* ``OT_ROLE_LEADER`` - The Thread Leader role + +**Note:** This is a static function. + +otGetStringDeviceRole +^^^^^^^^^^^^^^^^^^^^^ + +Gets the current device role as a string. + +.. code-block:: arduino + + static const char *otGetStringDeviceRole(); + +This function returns a human-readable string representation of the current device role. + +**Note:** This is a static function. + +otPrintNetworkInformation +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Prints network information to a Stream. + +.. code-block:: arduino + + static void otPrintNetworkInformation(Stream &output); + +* ``output`` - The Stream object to print to (e.g., ``Serial``) + +This function prints comprehensive network information including: +* Device role +* RLOC16 +* Network name +* Channel +* PAN ID +* Extended PAN ID +* Network key + +**Note:** This is a static function. + +Address Management +****************** + +getMeshLocalPrefix +^^^^^^^^^^^^^^^^^^ + +Gets the mesh-local prefix. + +.. code-block:: arduino + + const otMeshLocalPrefix *getMeshLocalPrefix() const; + +This function returns a pointer to the mesh-local prefix structure. + +getMeshLocalEid +^^^^^^^^^^^^^^^ + +Gets the mesh-local EID (Endpoint Identifier). + +.. code-block:: arduino + + IPAddress getMeshLocalEid() const; + +This function returns the mesh-local IPv6 address as an ``IPAddress`` object. + +getLeaderRloc +^^^^^^^^^^^^^ + +Gets the Thread Leader RLOC (Routing Locator). + +.. code-block:: arduino + + IPAddress getLeaderRloc() const; + +This function returns the IPv6 address of the Thread Leader as an ``IPAddress`` object. + +getRloc +^^^^^^^ + +Gets the node RLOC (Routing Locator). + +.. code-block:: arduino + + IPAddress getRloc() const; + +This function returns the IPv6 RLOC address of this node as an ``IPAddress`` object. + +getRloc16 +^^^^^^^^^ + +Gets the RLOC16 (16-bit Routing Locator). + +.. code-block:: arduino + + uint16_t getRloc16() const; + +This function returns the 16-bit RLOC of this node. + +Unicast Address Management +************************** + +getUnicastAddressCount +^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of unicast addresses. + +.. code-block:: arduino + + size_t getUnicastAddressCount() const; + +This function returns the number of unicast IPv6 addresses assigned to this node. The count is cached for performance. + +getUnicastAddress +^^^^^^^^^^^^^^^^^ + +Gets a unicast address by index. + +.. code-block:: arduino + + IPAddress getUnicastAddress(size_t index) const; + +* ``index`` - The index of the address (0-based) + +This function returns the unicast IPv6 address at the specified index as an ``IPAddress`` object. + +**Note:** Addresses are cached for performance. Use ``clearUnicastAddressCache()`` to refresh the cache. + +getAllUnicastAddresses +^^^^^^^^^^^^^^^^^^^^^^ + +Gets all unicast addresses. + +.. code-block:: arduino + + std::vector getAllUnicastAddresses() const; + +This function returns a vector containing all unicast IPv6 addresses assigned to this node. + +Multicast Address Management +**************************** + +getMulticastAddressCount +^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of multicast addresses. + +.. code-block:: arduino + + size_t getMulticastAddressCount() const; + +This function returns the number of multicast IPv6 addresses subscribed by this node. The count is cached for performance. + +getMulticastAddress +^^^^^^^^^^^^^^^^^^^ + +Gets a multicast address by index. + +.. code-block:: arduino + + IPAddress getMulticastAddress(size_t index) const; + +* ``index`` - The index of the address (0-based) + +This function returns the multicast IPv6 address at the specified index as an ``IPAddress`` object. + +**Note:** Addresses are cached for performance. Use ``clearMulticastAddressCache()`` to refresh the cache. + +getAllMulticastAddresses +^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets all multicast addresses. + +.. code-block:: arduino + + std::vector getAllMulticastAddresses() const; + +This function returns a vector containing all multicast IPv6 addresses subscribed by this node. + +Cache Management +**************** + +clearUnicastAddressCache +^^^^^^^^^^^^^^^^^^^^^^^^ + +Clears the unicast address cache. + +.. code-block:: arduino + + void clearUnicastAddressCache() const; + +This function clears the cached unicast addresses. The cache will be automatically repopulated on the next address access. + +clearMulticastAddressCache +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Clears the multicast address cache. + +.. code-block:: arduino + + void clearMulticastAddressCache() const; + +This function clears the cached multicast addresses. The cache will be automatically repopulated on the next address access. + +clearAllAddressCache +^^^^^^^^^^^^^^^^^^^^ + +Clears all address caches. + +.. code-block:: arduino + + void clearAllAddressCache() const; + +This function clears both unicast and multicast address caches. + +Advanced Access +*************** + +getInstance +^^^^^^^^^^^ + +Gets the OpenThread instance pointer. + +.. code-block:: arduino + + otInstance *getInstance(); + +This function returns a pointer to the underlying OpenThread instance. This allows direct access to OpenThread API functions for advanced use cases. + +**Warning:** Direct use of the OpenThread instance requires knowledge of the OpenThread API. Use with caution. + +Operators +********* + +bool operator +^^^^^^^^^^^^^ + +Returns whether OpenThread is started. + +.. code-block:: arduino + + operator bool() const; + +This operator returns ``true`` if OpenThread is started and ready, ``false`` otherwise. + +**Example:** + +.. code-block:: arduino + + if (OThread) { + Serial.println("OpenThread is ready"); + } + +Example +------- + +Basic Thread Network Setup +************************** + +.. code-block:: arduino + + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread stack + OpenThread::begin(); + + // Wait for OpenThread to be ready + while (!OThread) { + delay(100); + } + + // Create and configure dataset + DataSet dataset; + dataset.initNew(); + dataset.setNetworkName("MyThreadNetwork"); + dataset.setChannel(15); + + // Set network key (16 bytes) + uint8_t networkKey[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + + // Apply dataset and start network + OThread.commitDataSet(dataset); + OThread.start(); + OThread.networkInterfaceUp(); + + // Wait for network to be ready + while (OpenThread::otGetDeviceRole() == OT_ROLE_DETACHED) { + delay(100); + } + + // Print network information + OpenThread::otPrintNetworkInformation(Serial); + + // Get and print addresses + Serial.printf("Mesh Local EID: %s\r\n", OThread.getMeshLocalEid().toString().c_str()); + Serial.printf("RLOC: %s\r\n", OThread.getRloc().toString().c_str()); + Serial.printf("RLOC16: 0x%04x\r\n", OThread.getRloc16()); + } + +Monitoring Device Role +********************** + +.. code-block:: arduino + + void loop() { + ot_device_role_t role = OpenThread::otGetDeviceRole(); + const char *roleStr = OpenThread::otGetStringDeviceRole(); + + Serial.printf("Current role: %s\r\n", roleStr); + + switch (role) { + case OT_ROLE_LEADER: + Serial.println("This device is the Thread Leader"); + break; + case OT_ROLE_ROUTER: + Serial.println("This device is a Thread Router"); + break; + case OT_ROLE_CHILD: + Serial.println("This device is a Thread Child"); + break; + case OT_ROLE_DETACHED: + Serial.println("This device is not attached to a network"); + break; + case OT_ROLE_DISABLED: + Serial.println("Thread is disabled"); + break; + } + + delay(5000); + } + +Address Management +****************** + +.. code-block:: arduino + + void printAddresses() { + // Print unicast addresses + size_t unicastCount = OThread.getUnicastAddressCount(); + Serial.printf("Unicast addresses: %zu\r\n", unicastCount); + for (size_t i = 0; i < unicastCount; i++) { + Serial.printf(" [%zu] %s\r\n", i, OThread.getUnicastAddress(i).toString().c_str()); + } + + // Print multicast addresses + size_t multicastCount = OThread.getMulticastAddressCount(); + Serial.printf("Multicast addresses: %zu\r\n", multicastCount); + for (size_t i = 0; i < multicastCount; i++) { + Serial.printf(" [%zu] %s\r\n", i, OThread.getMulticastAddress(i).toString().c_str()); + } + + // Clear cache to force refresh + OThread.clearAllAddressCache(); + } diff --git a/docs/en/openthread/openthread_dataset.rst b/docs/en/openthread/openthread_dataset.rst new file mode 100644 index 00000000000..1cffd5cac2d --- /dev/null +++ b/docs/en/openthread/openthread_dataset.rst @@ -0,0 +1,461 @@ +############# +DataSet Class +############# + +About +----- + +The ``DataSet`` class provides a convenient way to create, configure, and manage Thread operational datasets. An operational dataset contains all the parameters needed to join or form a Thread network, including network name, channel, PAN ID, network key, and extended PAN ID. + +**Key Features:** +* Create new operational datasets +* Configure dataset parameters +* Apply datasets to the Thread network +* Retrieve dataset parameters +* Clear and reset datasets + +**Use Cases:** +* Creating new Thread networks +* Joining existing Thread networks +* Configuring network parameters +* Network migration and reconfiguration + +API Reference +------------- + +Constructor +*********** + +DataSet +^^^^^^^ + +Creates a new DataSet object. + +.. code-block:: arduino + + DataSet(); + +This constructor creates an empty dataset. You must either call ``initNew()`` to create a new dataset or configure parameters manually. + +Dataset Management +****************** + +clear +^^^^^ + +Clears all dataset parameters. + +.. code-block:: arduino + + void clear(); + +This function clears all dataset parameters, resetting the dataset to an empty state. + +initNew +^^^^^^^ + +Initializes a new operational dataset. + +.. code-block:: arduino + + void initNew(); + +This function creates a new operational dataset with randomly generated values for network key, extended PAN ID, and other parameters. The dataset will be ready to form a new Thread network. + +**Note:** OpenThread must be started (``OpenThread::begin()``) before calling this function. + +**Example:** + +.. code-block:: arduino + + DataSet dataset; + dataset.initNew(); // Creates new dataset with random values + dataset.setNetworkName("MyNewNetwork"); + dataset.setChannel(15); + +getDataset +^^^^^^^^^^ + +Gets the underlying OpenThread dataset structure. + +.. code-block:: arduino + + const otOperationalDataset &getDataset() const; + +This function returns a reference to the underlying ``otOperationalDataset`` structure. This is used internally when applying the dataset to the Thread network. + +**Note:** This function is typically used internally by ``OpenThread::commitDataSet()``. + +Setters +******* + +setNetworkName +^^^^^^^^^^^^^^ + +Sets the network name. + +.. code-block:: arduino + + void setNetworkName(const char *name); + +* ``name`` - The network name string (maximum 16 characters) + +This function sets the network name for the Thread network. The network name is a human-readable identifier for the network. + +**Example:** + +.. code-block:: arduino + + dataset.setNetworkName("MyThreadNetwork"); + +setExtendedPanId +^^^^^^^^^^^^^^^^ + +Sets the extended PAN ID. + +.. code-block:: arduino + + void setExtendedPanId(const uint8_t *extPanId); + +* ``extPanId`` - Pointer to an 8-byte array containing the extended PAN ID + +This function sets the extended PAN ID, which uniquely identifies the Thread network partition. + +**Example:** + +.. code-block:: arduino + + uint8_t extPanId[8] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; + dataset.setExtendedPanId(extPanId); + +setNetworkKey +^^^^^^^^^^^^^ + +Sets the network key. + +.. code-block:: arduino + + void setNetworkKey(const uint8_t *key); + +* ``key`` - Pointer to a 16-byte array containing the network key + +This function sets the network key, which is used for encryption and authentication in the Thread network. + +**Example:** + +.. code-block:: arduino + + uint8_t networkKey[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + +setChannel +^^^^^^^^^^ + +Sets the Thread channel. + +.. code-block:: arduino + + void setChannel(uint8_t channel); + +* ``channel`` - The Thread channel number (11-26) + +This function sets the IEEE 802.15.4 channel used by the Thread network. Valid channels are 11 through 26. + +**Example:** + +.. code-block:: arduino + + dataset.setChannel(15); // Use channel 15 + +setPanId +^^^^^^^^ + +Sets the PAN ID. + +.. code-block:: arduino + + void setPanId(uint16_t panId); + +* ``panId`` - The PAN ID (16-bit value) + +This function sets the PAN (Personal Area Network) ID for the Thread network. + +**Example:** + +.. code-block:: arduino + + dataset.setPanId(0x1234); // Set PAN ID to 0x1234 + +Getters +******* + +getNetworkName +^^^^^^^^^^^^^^ + +Gets the network name. + +.. code-block:: arduino + + const char *getNetworkName() const; + +This function returns a pointer to the network name string. + +**Returns:** Pointer to the network name, or ``NULL`` if not set. + +getExtendedPanId +^^^^^^^^^^^^^^^^ + +Gets the extended PAN ID. + +.. code-block:: arduino + + const uint8_t *getExtendedPanId() const; + +This function returns a pointer to the 8-byte extended PAN ID array. + +**Returns:** Pointer to the extended PAN ID array, or ``NULL`` if not set. + +getNetworkKey +^^^^^^^^^^^^^ + +Gets the network key. + +.. code-block:: arduino + + const uint8_t *getNetworkKey() const; + +This function returns a pointer to the 16-byte network key array. + +**Returns:** Pointer to the network key array, or ``NULL`` if not set. + +getChannel +^^^^^^^^^^ + +Gets the Thread channel. + +.. code-block:: arduino + + uint8_t getChannel() const; + +This function returns the Thread channel number. + +**Returns:** The channel number (11-26), or 0 if not set. + +getPanId +^^^^^^^^ + +Gets the PAN ID. + +.. code-block:: arduino + + uint16_t getPanId() const; + +This function returns the PAN ID. + +**Returns:** The PAN ID, or 0 if not set. + +Dataset Application +******************* + +apply +^^^^^ + +Applies the dataset to an OpenThread instance. + +.. code-block:: arduino + + void apply(otInstance *instance); + +* ``instance`` - Pointer to the OpenThread instance + +This function applies the dataset to the specified OpenThread instance, making it the active operational dataset. + +**Note:** This function is typically used internally. For normal use, use ``OpenThread::commitDataSet()`` instead. + +**Example:** + +.. code-block:: arduino + + otInstance *instance = OThread.getInstance(); + dataset.apply(instance); + +Example +------- + +Creating a New Network +********************** + +.. code-block:: arduino + + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Create a new dataset + DataSet dataset; + dataset.initNew(); // Generate random values + + // Configure network parameters + dataset.setNetworkName("MyNewThreadNetwork"); + dataset.setChannel(15); + + // Set network key (16 bytes) + uint8_t networkKey[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + + // Set extended PAN ID (8 bytes) + uint8_t extPanId[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + dataset.setExtendedPanId(extPanId); + + // Set PAN ID + dataset.setPanId(0x1234); + + // Apply dataset and start network + OThread.commitDataSet(dataset); + OThread.start(); + OThread.networkInterfaceUp(); + + Serial.println("New Thread network created"); + } + +Joining an Existing Network +*************************** + +.. code-block:: arduino + + #include + + void setup() { + Serial.begin(115200); + + // Initialize OpenThread + OpenThread::begin(); + while (!OThread) { + delay(100); + } + + // Create dataset for existing network + DataSet dataset; + + // Configure with existing network parameters + dataset.setNetworkName("ExistingThreadNetwork"); + dataset.setChannel(15); + + // Set the network key from the existing network + uint8_t networkKey[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + dataset.setNetworkKey(networkKey); + + // Set the extended PAN ID from the existing network + uint8_t extPanId[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + dataset.setExtendedPanId(extPanId); + + // Set PAN ID + dataset.setPanId(0x1234); + + // Apply dataset and start network + OThread.commitDataSet(dataset); + OThread.start(); + OThread.networkInterfaceUp(); + + Serial.println("Joining existing Thread network"); + } + +Reading Current Dataset +*********************** + +.. code-block:: arduino + + void printCurrentDataset() { + // Get current dataset from OpenThread + const DataSet ¤tDataset = OThread.getCurrentDataSet(); + + // Print dataset parameters + Serial.println("Current Thread Dataset:"); + Serial.printf(" Network Name: %s\r\n", currentDataset.getNetworkName()); + Serial.printf(" Channel: %d\r\n", currentDataset.getChannel()); + Serial.printf(" PAN ID: 0x%04x\r\n", currentDataset.getPanId()); + + // Print extended PAN ID + const uint8_t *extPanId = currentDataset.getExtendedPanId(); + if (extPanId) { + Serial.print(" Extended PAN ID: "); + for (int i = 0; i < 8; i++) { + Serial.printf("%02x", extPanId[i]); + } + Serial.println(); + } + + // Print network key (first 4 bytes for security) + const uint8_t *networkKey = currentDataset.getNetworkKey(); + if (networkKey) { + Serial.print(" Network Key: "); + for (int i = 0; i < 4; i++) { + Serial.printf("%02x", networkKey[i]); + } + Serial.println("..."); + } + } + +Modifying Dataset Parameters +**************************** + +.. code-block:: arduino + + void modifyNetworkChannel() { + // Get current dataset + DataSet dataset = OThread.getCurrentDataSet(); + + // Modify channel + dataset.setChannel(20); // Change to channel 20 + + // Apply modified dataset + OThread.commitDataSet(dataset); + + Serial.println("Network channel changed to 20"); + } + +Best Practices +-------------- + +Dataset Security +**************** + +* **Network Key**: Keep the network key secure and never expose it in logs or serial output +* **Extended PAN ID**: Use unique extended PAN IDs to avoid network conflicts +* **Channel Selection**: Choose channels that avoid interference with Wi-Fi networks (channels 11, 15, 20, 25 are often good choices) + +Required Parameters +******************* + +For a dataset to be valid and usable, you typically need: + +* **Network Name**: Required for human identification +* **Network Key**: Required for security (16 bytes) +* **Channel**: Required for radio communication (11-26) +* **Extended PAN ID**: Recommended for network identification (8 bytes) +* **PAN ID**: Optional, will be assigned if not set + +Parameter Validation +******************** + +The DataSet class performs basic validation: + +* Network name length is checked (maximum 16 characters) +* Channel range is validated (11-26) +* Null pointer checks for array parameters + +However, it's your responsibility to ensure: + +* Network key is properly generated (use ``initNew()`` for random generation) +* Extended PAN ID is unique within your network environment +* All required parameters are set before applying the dataset diff --git a/docs/en/troubleshooting.rst b/docs/en/troubleshooting.rst index ea9a6db94d6..3d632d560bb 100644 --- a/docs/en/troubleshooting.rst +++ b/docs/en/troubleshooting.rst @@ -149,7 +149,7 @@ Solution ^^^^^^^^ Newer ESP32 variants have two possible USB connectors - USB and UART. The UART connector will go through a USB->UART adapter, and will typically present itself with the name of that mfr (eg, Silicon Labs CP210x UART Bridge). The USB connector can be used as a USB-CDC bridge and will appear as an Espressif device (Espressif USB JTAG/serial debug unit). On Espressif devkits, both connections are available, and will be labeled. ESP32 can only use UART, so will only have one connector. Other variants with one connector will typically be using USB. Please check in the product [datasheet](https://products.espressif.com) or [hardware guide](https://www.espressif.com/en/products/devkits) to find Espressif products with the appropriate USB connections for your needs. -If you use the UART connector, you should disable USB-CDC on boot under the Tools menu (-D ARDUINO_USB_CDC_ON_BOOT=0). If you use the USB connector, you should have that enabled (-D ARDUINO_USB_CDC_ON_BOOT=1) and set USB Mode to "Hardware CDC and JTAG" (-D ARDUINO_USB_MODE=0). +If you use the UART connector, you should disable USB-CDC on boot under the Tools menu (-D ARDUINO_USB_CDC_ON_BOOT=0). If you use the USB connector, you should have that enabled (-D ARDUINO_USB_CDC_ON_BOOT=1) and set USB Mode to "Hardware CDC and JTAG" (-D ARDUINO_USB_MODE=1). USB-CDC may not be able to initialize in time to catch all the data if your device is in a tight reboot loop. This can make it difficult to troubleshoot initialization issues. SPIFFS mount failed diff --git a/docs/en/zigbee/ep_binary.rst b/docs/en/zigbee/ep_binary.rst index 950e20ef42b..06d65d05416 100644 --- a/docs/en/zigbee/ep_binary.rst +++ b/docs/en/zigbee/ep_binary.rst @@ -7,10 +7,7 @@ About The ``ZigbeeBinary`` class provides an endpoint for binary input/output sensors in Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for binary sensors, supporting various application types for HVAC, security, and general binary sensing. Binary Input (BI) is meant to be used for sensors that provide a binary signal, such as door/window sensors, motion detectors, etc. to be sent to the network. - -.. note:: - - Binary Output (BO) is not supported yet. +Binary Output (BO) is used for controlling binary devices such as relays, switches, or actuators that can be turned on/off remotely. API Reference ------------- @@ -63,6 +60,33 @@ Security Application Types #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF +Binary Output Application Types +******************************* + +HVAC Application Types +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_BOILER 0x00000003 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_CHILLER 0x0000000D + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN 0x00000022 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HEATING_VALVE 0x0000002C + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER 0x00000033 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_PREHEAT 0x00000034 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF + +Security Application Types +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ARM_DISARM_COMMAND 0x01000000 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OCCUPANCY_CONTROL 0x01000001 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ENABLE_CONTROL 0x01000002 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ACCESS_CONTROL 0x01000003 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF + API Methods *********** @@ -127,11 +151,94 @@ Manually reports the current binary input value. This function will return ``true`` if successful, ``false`` otherwise. +addBinaryOutput +^^^^^^^^^^^^^^^ + +Adds a binary output cluster to the endpoint. + +.. code-block:: arduino + + bool addBinaryOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +setBinaryOutputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the binary output. + +.. code-block:: arduino + + bool setBinaryOutputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Binary Output Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setBinaryOutputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the binary output. + +.. code-block:: arduino + + bool setBinaryOutputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +onBinaryOutputChange +^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when the binary output value changes. + +.. code-block:: arduino + + void onBinaryOutputChange(void (*callback)(bool binary_output)); + +* ``callback`` - Function pointer to callback that receives the new binary output value + +setBinaryOutput +^^^^^^^^^^^^^^^ + +Sets the binary output value. + +.. code-block:: arduino + + bool setBinaryOutput(bool output); + +* ``output`` - Binary value (true/false) + +This function will return ``true`` if successful, ``false`` otherwise. + +getBinaryOutput +^^^^^^^^^^^^^^^ + +Gets the current binary output value. + +.. code-block:: arduino + + bool getBinaryOutput(); + +This function returns the current binary output state. + +reportBinaryOutput +^^^^^^^^^^^^^^^^^^ + +Manually reports the current binary output value. + +.. code-block:: arduino + + bool reportBinaryOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + Example ------- -Binary Input Implementation -**************************** +Binary Input and Output Implementation +************************************** -.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino +.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino :language: arduino diff --git a/docs/en/zigbee/ep_color_dimmable_light.rst b/docs/en/zigbee/ep_color_dimmable_light.rst index b2fa6d0bf91..46a785424e2 100644 --- a/docs/en/zigbee/ep_color_dimmable_light.rst +++ b/docs/en/zigbee/ep_color_dimmable_light.rst @@ -5,13 +5,17 @@ ZigbeeColorDimmableLight About ----- -The ``ZigbeeColorDimmableLight`` class provides an endpoint for color dimmable lights in Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for color lighting devices, supporting RGB color control, dimming, and scene management. +The ``ZigbeeColorDimmableLight`` class provides an endpoint for color dimmable lights in Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for color lighting devices, supporting multiple color modes (RGB/XY, HSV, and Color Temperature), dimming, and scene management. **Features:** * On/off control -* Brightness level control (0-100%) -* RGB color control -* HSV color support +* Brightness level control (0-255) +* RGB/XY color control +* HSV (Hue/Saturation) color support +* Color temperature (mireds) support +* Configurable color capabilities (enable/disable color modes) +* Separate callbacks for RGB, HSV, and Temperature modes +* Automatic color mode switching * Scene and group support * Automatic state restoration * Integration with common endpoint features (binding, OTA, etc.) @@ -20,6 +24,8 @@ The ``ZigbeeColorDimmableLight`` class provides an endpoint for color dimmable l **Use Cases:** * Smart RGB light bulbs * Color-changing LED strips +* Tunable white light bulbs +* Full-spectrum color temperature lights * Mood lighting systems * Entertainment lighting * Architectural lighting @@ -42,13 +48,66 @@ Creates a new Zigbee color dimmable light endpoint. * ``endpoint`` - Endpoint number (1-254) +Color Capabilities +****************** + +setLightColorCapabilities +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Configures which color modes are supported by the light. Must be called before starting Zigbee. By default, only XY (RGB) mode is enabled. + +.. code-block:: arduino + + bool setLightColorCapabilities(uint16_t capabilities); + +* ``capabilities`` - Bit flags indicating supported color modes (can be combined with bitwise OR): + + * ``ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION`` - Hue/Saturation support + * ``ZIGBEE_COLOR_CAPABILITY_X_Y`` - XY (RGB) support + * ``ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP`` - Color temperature support + * ``ZIGBEE_COLOR_CAPABILITY_ENHANCED_HUE`` - Enhanced hue support + * ``ZIGBEE_COLOR_CAPABILITY_COLOR_LOOP`` - Color loop support + +**Example:** + +.. code-block:: arduino + + // Enable XY and Temperature modes + light.setLightColorCapabilities( + ZIGBEE_COLOR_CAPABILITY_X_Y | ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP + ); + + // Enable all color modes + light.setLightColorCapabilities( + ZIGBEE_COLOR_CAPABILITY_X_Y | + ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION | + ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP + ); + +This function will return ``true`` if successful, ``false`` otherwise. + Callback Functions ****************** +Callback Type Definitions +^^^^^^^^^^^^^^^^^^^^^^^^^ + +For better type safety and readability, typedefs are provided for all callback functions: + +.. code-block:: arduino + + typedef void (*ZigbeeColorLightRgbCallback)(bool state, uint8_t red, uint8_t green, uint8_t blue, uint8_t level); + typedef void (*ZigbeeColorLightHsvCallback)(bool state, uint8_t hue, uint8_t saturation, uint8_t value); + typedef void (*ZigbeeColorLightTempCallback)(bool state, uint8_t level, uint16_t color_temperature); + +These typedefs can be used instead of raw function pointer syntax for better code clarity. + onLightChange ^^^^^^^^^^^^^ -Sets the callback function for light state changes. +.. deprecated:: This method is deprecated and will be removed in a future major version. Use ``onLightChangeRgb()`` instead. + +Sets the legacy callback function for light state changes (RGB mode). .. code-block:: arduino @@ -56,6 +115,69 @@ Sets the callback function for light state changes. * ``callback`` - Function pointer to the light change callback (state, red, green, blue, level) + * ``state`` - Light state (true = on, false = off) + * ``red`` - Red component (0-255) + * ``green`` - Green component (0-255) + * ``blue`` - Blue component (0-255) + * ``level`` - Brightness level (0-255) + +.. note:: + This method is deprecated. Please use ``onLightChangeRgb()`` for RGB/XY mode callbacks. + +onLightChangeRgb +^^^^^^^^^^^^^^^^ + +Sets the callback function for RGB/XY color mode changes. + +.. code-block:: arduino + + void onLightChangeRgb(ZigbeeColorLightRgbCallback callback); + // or using raw function pointer syntax: + void onLightChangeRgb(void (*callback)(bool, uint8_t, uint8_t, uint8_t, uint8_t)); + +* ``callback`` - Function pointer to the RGB light change callback (state, red, green, blue, level) + + * ``state`` - Light state (true = on, false = off) + * ``red`` - Red component (0-255) + * ``green`` - Green component (0-255) + * ``blue`` - Blue component (0-255) + * ``level`` - Brightness level (0-255) + +onLightChangeHsv +^^^^^^^^^^^^^^^^ + +Sets the callback function for HSV (Hue/Saturation) color mode changes. + +.. code-block:: arduino + + void onLightChangeHsv(ZigbeeColorLightHsvCallback callback); + // or using raw function pointer syntax: + void onLightChangeHsv(void (*callback)(bool, uint8_t, uint8_t, uint8_t)); + +* ``callback`` - Function pointer to the HSV light change callback (state, hue, saturation, value) + + * ``state`` - Light state (true = on, false = off) + * ``hue`` - Hue component (0-254) + * ``saturation`` - Saturation component (0-254) + * ``value`` - Value/brightness component (0-255) + +onLightChangeTemp +^^^^^^^^^^^^^^^^^ + +Sets the callback function for color temperature mode changes. + +.. code-block:: arduino + + void onLightChangeTemp(ZigbeeColorLightTempCallback callback); + // or using raw function pointer syntax: + void onLightChangeTemp(void (*callback)(bool, uint8_t, uint16_t)); + +* ``callback`` - Function pointer to the temperature light change callback (state, level, temperature_mireds) + + * ``state`` - Light state (true = on, false = off) + * ``level`` - Brightness level (0-255) + * ``temperature_mireds`` - Color temperature in mireds (inverse of Kelvin) + Control Methods *************** @@ -81,14 +203,14 @@ Sets the light brightness level. bool setLightLevel(uint8_t level); -* ``level`` - Brightness level (0-100, where 0 is off, 100 is full brightness) +* ``level`` - Brightness level (0-255, where 0 is off, 255 is full brightness) This function will return ``true`` if successful, ``false`` otherwise. setLightColor (RGB) ^^^^^^^^^^^^^^^^^^^ -Sets the light color using RGB values. +Sets the light color using RGB values. Requires ``ZIGBEE_COLOR_CAPABILITY_X_Y`` capability to be enabled. .. code-block:: arduino @@ -100,12 +222,12 @@ Sets the light color using RGB values. * ``blue`` - Blue component (0-255) * ``rgb_color`` - RGB color structure -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if successful, ``false`` otherwise. Returns ``false`` if XY capability is not enabled. setLightColor (HSV) ^^^^^^^^^^^^^^^^^^^ -Sets the light color using HSV values. +Sets the light color using HSV values. Requires ``ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION`` capability to be enabled. .. code-block:: arduino @@ -113,24 +235,72 @@ Sets the light color using HSV values. * ``hsv_color`` - HSV color structure +This function will return ``true`` if successful, ``false`` otherwise. Returns ``false`` if HSV capability is not enabled. + +setLightColorTemperature +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the light color temperature in mireds. Requires ``ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP`` capability to be enabled. + +.. code-block:: arduino + + bool setLightColorTemperature(uint16_t color_temperature); + +* ``color_temperature`` - Color temperature in mireds (inverse of Kelvin: mireds = 1000000 / Kelvin) + +**Example:** + +.. code-block:: arduino + + // Set to 4000K (cool white) + uint16_t mireds = 1000000 / 4000; // = 250 mireds + light.setLightColorTemperature(mireds); + + // Set to 2700K (warm white) + mireds = 1000000 / 2700; // = 370 mireds + light.setLightColorTemperature(mireds); + +This function will return ``true`` if successful, ``false`` otherwise. Returns ``false`` if color temperature capability is not enabled. + +setLightColorTemperatureRange +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the minimum and maximum color temperature range supported by the hardware. + +.. code-block:: arduino + + bool setLightColorTemperatureRange(uint16_t min_temp, uint16_t max_temp); + +* ``min_temp`` - Minimum color temperature in mireds +* ``max_temp`` - Maximum color temperature in mireds + +**Example:** + +.. code-block:: arduino + + // Set range for 2000K (warm) to 6500K (cool) + uint16_t min_mireds = 1000000 / 6500; // = 154 mireds + uint16_t max_mireds = 1000000 / 2000; // = 500 mireds + light.setLightColorTemperatureRange(min_mireds, max_mireds); + This function will return ``true`` if successful, ``false`` otherwise. setLight ^^^^^^^^ -Sets all light parameters at once. +Sets all light parameters at once (RGB mode). Requires ``ZIGBEE_COLOR_CAPABILITY_X_Y`` capability to be enabled. .. code-block:: arduino bool setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue); * ``state`` - Light state (true/false) -* ``level`` - Brightness level (0-100) +* ``level`` - Brightness level (0-255) * ``red`` - Red component (0-255) * ``green`` - Green component (0-255) * ``blue`` - Blue component (0-255) -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if successful, ``false`` otherwise. Returns ``false`` if XY capability is not enabled. State Retrieval Methods *********************** @@ -155,7 +325,7 @@ Gets the current brightness level. uint8_t getLightLevel(); -This function will return current brightness level (0-100). +This function will return current brightness level (0-255). getLightColor ^^^^^^^^^^^^^ @@ -201,18 +371,117 @@ Gets the current blue component. This function will return current blue component (0-255). +getLightColorTemperature +^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current color temperature. + +.. code-block:: arduino + + uint16_t getLightColorTemperature(); + +This function will return current color temperature in mireds. + +getLightColorMode +^^^^^^^^^^^^^^^^^ + +Gets the current active color mode. + +.. code-block:: arduino + + uint8_t getLightColorMode(); + +This function will return current color mode: +* ``ZIGBEE_COLOR_MODE_HUE_SATURATION`` (0x00) - HSV mode +* ``ZIGBEE_COLOR_MODE_CURRENT_X_Y`` (0x01) - XY/RGB mode +* ``ZIGBEE_COLOR_MODE_TEMPERATURE`` (0x02) - Temperature mode + +getLightColorHue +^^^^^^^^^^^^^^^^ + +Gets the current hue value (HSV mode). + +.. code-block:: arduino + + uint8_t getLightColorHue(); + +This function will return current hue value (0-254). + +getLightColorSaturation +^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current saturation value (HSV mode). + +.. code-block:: arduino + + uint8_t getLightColorSaturation(); + +This function will return current saturation value (0-254). + +getLightColorCapabilities +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the currently configured color capabilities. + +.. code-block:: arduino + + uint16_t getLightColorCapabilities(); + +This function will return the current color capabilities bit flags. + Utility Methods *************** restoreLight ^^^^^^^^^^^^ -Restores the light to its last known state. +Restores the light to its last known state. Uses the appropriate callback based on the current color mode. .. code-block:: arduino void restoreLight(); +Color Modes and Automatic Behavior +********************************** + +The ``ZigbeeColorDimmableLight`` class supports three color modes: + +* **XY/RGB Mode** (``ZIGBEE_COLOR_MODE_CURRENT_X_Y``) - Uses X/Y coordinates for color representation, internally converted to RGB +* **HSV Mode** (``ZIGBEE_COLOR_MODE_HUE_SATURATION``) - Uses Hue and Saturation values directly, without RGB conversion +* **Temperature Mode** (``ZIGBEE_COLOR_MODE_TEMPERATURE``) - Uses color temperature in mireds + +**Automatic Mode Switching:** + +The color mode is automatically updated based on which attributes are set: + +* Setting RGB colors via ``setLight()`` or ``setLightColor()`` (RGB) → switches to XY mode +* Setting HSV colors via ``setLightColor()`` (HSV) → switches to HSV mode +* Setting temperature via ``setLightColorTemperature()`` → switches to Temperature mode +* Receiving Zigbee commands for XY/HSV/TEMP attributes → automatically switches to the corresponding mode + +**Capability Validation:** + +All set methods validate that the required capability is enabled before allowing the operation: + +* RGB/XY methods require ``ZIGBEE_COLOR_CAPABILITY_X_Y`` +* HSV methods require ``ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION`` +* Temperature methods require ``ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP`` + +If a capability is not enabled, the method will return ``false`` and log an error. + +**Callback Selection:** + +The appropriate callback is automatically called based on the current color mode: + +* RGB/XY mode → ``onLightChangeRgb()`` callback +* HSV mode → ``onLightChangeHsv()`` callback +* Temperature mode → ``onLightChangeTemp()`` callback + +When level or state changes occur, the callback for the current color mode is used automatically. + +.. note:: + The legacy ``onLightChange()`` callback is deprecated and will be removed in a future major version. Always use the mode-specific callbacks (``onLightChangeRgb()``, ``onLightChangeHsv()``, or ``onLightChangeTemp()``). + Example ------- diff --git a/docs/en/zigbee/ep_contact_switch.rst b/docs/en/zigbee/ep_contact_switch.rst index f7f6dc15c66..50844cdd180 100644 --- a/docs/en/zigbee/ep_contact_switch.rst +++ b/docs/en/zigbee/ep_contact_switch.rst @@ -63,6 +63,17 @@ Sets the contact switch to open state. This function will return ``true`` if successful, ``false`` otherwise. +report +^^^^^^ + +Manually reports the current contact state. + +.. code-block:: arduino + + bool report(); + +This function will return ``true`` if successful, ``false`` otherwise. + setIASClientEndpoint ^^^^^^^^^^^^^^^^^^^^ @@ -74,16 +85,38 @@ Sets the IAS Client endpoint number (default is 1). * ``ep_number`` - IAS Client endpoint number -report -^^^^^^ +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ -Manually reports the current contact state. +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. .. code-block:: arduino - bool report(); + bool requestIASZoneEnroll(); -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/docs/en/zigbee/ep_door_window_handle.rst b/docs/en/zigbee/ep_door_window_handle.rst index 53203f463dd..b2339d681a5 100644 --- a/docs/en/zigbee/ep_door_window_handle.rst +++ b/docs/en/zigbee/ep_door_window_handle.rst @@ -67,6 +67,17 @@ Sets the door/window handle to tilted position. This function will return ``true`` if successful, ``false`` otherwise. +report +^^^^^^ + +Manually reports the current handle position. + +.. code-block:: arduino + + bool report(); + +This function will return ``true`` if successful, ``false`` otherwise. + setIASClientEndpoint ^^^^^^^^^^^^^^^^^^^^ @@ -78,16 +89,38 @@ Sets the IAS Client endpoint number (default is 1). * ``ep_number`` - IAS Client endpoint number -report -^^^^^^ +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ -Manually reports the current handle position. +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. .. code-block:: arduino - bool report(); + bool requestIASZoneEnroll(); -This function will return ``true`` if successful, ``false`` otherwise. +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/docs/en/zigbee/ep_multistate.rst b/docs/en/zigbee/ep_multistate.rst new file mode 100644 index 00000000000..004aeaf5660 --- /dev/null +++ b/docs/en/zigbee/ep_multistate.rst @@ -0,0 +1,346 @@ +################ +ZigbeeMultistate +################ + +About +----- + +The ``ZigbeeMultistate`` class provides multistate input and output endpoints for Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for multistate signal processing and control. +Multistate Input (MI) is meant to be used for sensors that provide discrete state values, such as fan speed settings, HVAC modes, or security states to be sent to the coordinator. +Multistate Output (MO) is meant to be used for actuators that require discrete state control, such as fan controllers, HVAC systems, or security devices to be controlled by the coordinator. + +.. note:: + + HomeAssistant ZHA does not support multistate input and output clusters yet. + +Common API +---------- + +Constructor +*********** + +ZigbeeMultistate +^^^^^^^^^^^^^^^^ + +Creates a new Zigbee multistate endpoint. + +.. code-block:: arduino + + ZigbeeMultistate(uint8_t endpoint); + +* ``endpoint`` - Endpoint number (1-254) + +Multistate Application Types +**************************** + +The ZigbeeMultistate class supports predefined application types with specific state configurations: + +.. code-block:: arduino + + // Application Type 0: Fan states (Off, On, Auto) + #define ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX 0x0000 + #define ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES (const char* const[]){"Off", "On", "Auto"} + + // Application Type 1: Fan speed states (Off, Low, Medium, High) + #define ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX 0x0001 + #define ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES (const char* const[]){"Off", "Low", "Medium", "High"} + + // Application Type 2: HVAC modes (Auto, Heat, Cool, Off, Emergency Heat, Fan Only, Max Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX 0x0002 + #define ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES 7 + #define ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES (const char* const[]){"Auto", "Heat", "Cool", "Off", "Emergency Heat", "Fan Only", "Max Heat"} + + // Application Type 3: Occupancy states (Occupied, Unoccupied, Standby, Bypass) + #define ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX 0x0003 + #define ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES (const char* const[]){"Occupied", "Unoccupied", "Standby", "Bypass"} + + // Application Type 4: Control states (Inactive, Active, Hold) + #define ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX 0x0004 + #define ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES (const char* const[]){"Inactive", "Active", "Hold"} + + // Application Type 5: Water system states (Auto, Warm-up, Water Flush, Autocalibration, Shutdown Open, Shutdown Closed, Low Limit, Test and Balance) + #define ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX 0x0005 + #define ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES 8 + #define ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES (const char* const[]){"Auto", "Warm-up", "Water Flush", "Autocalibration", "Shutdown Open", "Shutdown Closed", "Low Limit", "Test and Balance"} + + // Application Type 6: HVAC system states (Off, Auto, Heat Cool, Heat Only, Cool Only, Fan Only) + #define ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX 0x0006 + #define ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES 6 + #define ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES (const char* const[]){"Off", "Auto", "Heat Cool", "Heat Only", "Cool Only", "Fan Only"} + + // Application Type 7: Light states (High, Normal, Low) + #define ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX 0x0007 + #define ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES (const char* const[]){"High", "Normal", "Low"} + + // Application Type 8: Occupancy control states (Occupied, Unoccupied, Startup, Shutdown) + #define ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX 0x0008 + #define ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES (const char* const[]){"Occupied", "Unoccupied", "Startup", "Shutdown"} + + // Application Type 9: Time states (Night, Day, Hold) + #define ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX 0x0009 + #define ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES (const char* const[]){"Night", "Day", "Hold"} + + // Application Type 10: HVAC control states (Off, Cool, Heat, Auto, Emergency Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX 0x000A + #define ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES 5 + #define ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES (const char* const[]){"Off", "Cool", "Heat", "Auto", "Emergency Heat"} + + // Application Type 11: Water control states (Shutdown Closed, Shutdown Open, Satisfied, Mixing, Cooling, Heating, Suppl Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX 0x000B + #define ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES 7 + #define ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES (const char* const[]){"Shutdown Closed", "Shutdown Open", "Satisfied", "Mixing", "Cooling", "Heating", "Suppl Heat"} + + // Custom application type for user-defined states + #define ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX 0xFFFF + +Cluster Management +****************** + +addMultistateInput +^^^^^^^^^^^^^^^^^^ + +Adds multistate input cluster to the endpoint. + +.. code-block:: arduino + + bool addMultistateInput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +addMultistateOutput +^^^^^^^^^^^^^^^^^^^ + +Adds multistate output cluster to the endpoint. + +.. code-block:: arduino + + bool addMultistateOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Multistate Input API +-------------------- + +Configuration Methods +********************* + +setMultistateInputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Multistate Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateInputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateInputStates +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the number of states for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputStates(uint16_t number_of_states); + +* ``number_of_states`` - Number of discrete states (1-65535) + +This function will return ``true`` if successful, ``false`` otherwise. + +Value Control +************* + +setMultistateInput +^^^^^^^^^^^^^^^^^^ + +Sets the multistate input value. + +.. code-block:: arduino + + bool setMultistateInput(uint16_t state); + +* ``state`` - State index (0 to number_of_states-1) + +This function will return ``true`` if successful, ``false`` otherwise. + +getMultistateInput +^^^^^^^^^^^^^^^^^^ + +Gets the current multistate input value. + +.. code-block:: arduino + + uint16_t getMultistateInput(); + +This function returns the current multistate input state. + +Reporting Methods +***************** + +reportMultistateInput +^^^^^^^^^^^^^^^^^^^^^ + +Manually reports the current multistate input value. + +.. code-block:: arduino + + bool reportMultistateInput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Multistate Output API +--------------------- + +Configuration Methods +********************* + +setMultistateOutputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Multistate Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateOutputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateOutputStates +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the number of states for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputStates(uint16_t number_of_states); + +* ``number_of_states`` - Number of discrete states (1-65535) + +This function will return ``true`` if successful, ``false`` otherwise. + +Value Control +************* + +setMultistateOutput +^^^^^^^^^^^^^^^^^^^^ + +Sets the multistate output value. + +.. code-block:: arduino + + bool setMultistateOutput(uint16_t state); + +* ``state`` - State index (0 to number_of_states-1) + +This function will return ``true`` if successful, ``false`` otherwise. + +getMultistateOutput +^^^^^^^^^^^^^^^^^^^ + +Gets the current multistate output value. + +.. code-block:: arduino + + uint16_t getMultistateOutput(); + +This function returns the current multistate output state. + +State Information +***************** + +getMultistateInputStateNamesLength +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of state names for multistate input. + +.. code-block:: arduino + + uint16_t getMultistateInputStateNamesLength(); + +This function returns the number of state names configured for multistate input. + +getMultistateOutputStateNamesLength +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of state names for multistate output. + +.. code-block:: arduino + + uint16_t getMultistateOutputStateNamesLength(); + +This function returns the number of state names configured for multistate output. + +Reporting Methods +***************** + +reportMultistateOutput +^^^^^^^^^^^^^^^^^^^^^^^ + +Manually reports the current multistate output value. + +.. code-block:: arduino + + bool reportMultistateOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Event Handling +************** + +onMultistateOutputChange +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when the multistate output value changes. + +.. code-block:: arduino + + void onMultistateOutputChange(void (*callback)(uint16_t state)); + +* ``callback`` - Function to call when multistate output changes, receives the new state value + +Example +------- + +Multistate Input/Output +*********************** + +.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino + :language: arduino diff --git a/docs/en/zigbee/ep_thermostat.rst b/docs/en/zigbee/ep_thermostat.rst index b7c79254d0f..b7b616b54e7 100644 --- a/docs/en/zigbee/ep_thermostat.rst +++ b/docs/en/zigbee/ep_thermostat.rst @@ -5,13 +5,14 @@ ZigbeeThermostat About ----- -The ``ZigbeeThermostat`` class provides a thermostat endpoint for Zigbee networks that receives temperature data from temperature sensors. This endpoint implements the Zigbee Home Automation (HA) standard for thermostats that can bind to temperature sensors and receive temperature readings. +The ``ZigbeeThermostat`` class provides a thermostat endpoint for Zigbee networks. +This endpoint implements the Zigbee Home Automation (HA) standard for thermostats that can bind to temperature and humidity sensors and receive temperature and humidity readings. **Features:** * Automatic discovery and binding to temperature sensors -* Temperature data reception from bound sensors -* Configurable temperature reporting intervals -* Sensor settings retrieval (min/max temperature, tolerance) +* Temperature and humidity data reception from bound sensors +* Configurable temperature and humidity reporting intervals +* Sensor settings retrieval (min/max/tolerance for temperature and humidity) * Multiple addressing modes (group, specific endpoint, IEEE address) API Reference @@ -60,20 +61,60 @@ Sets a callback function for receiving temperature data with source information. * ``src_endpoint`` - Source endpoint that sent the temperature data * ``src_address`` - Source address information -onConfigReceive -^^^^^^^^^^^^^^^ +onTempConfigReceive +^^^^^^^^^^^^^^^^^^^ Sets a callback function for receiving sensor configuration data. .. code-block:: arduino - void onConfigReceive(void (*callback)(float min_temp, float max_temp, float tolerance)); + void onTempConfigReceive(void (*callback)(float min_temp, float max_temp, float tolerance)); * ``callback`` - Function to call when sensor configuration is received * ``min_temp`` - Minimum temperature supported by the sensor * ``max_temp`` - Maximum temperature supported by the sensor * ``tolerance`` - Temperature tolerance of the sensor +onHumidityReceive +^^^^^^^^^^^^^^^^^ + +Sets a callback function for receiving humidity data. + +.. code-block:: arduino + + void onHumidityReceive(void (*callback)(float humidity)); + +* ``callback`` - Function to call when humidity data is received +* ``humidity`` - Humidity value in percentage + +onHumidityReceiveWithSource +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function for receiving humidity data with source information. + +.. code-block:: arduino + + void onHumidityReceiveWithSource(void (*callback)(float humidity, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address)); + +* ``callback`` - Function to call when humidity data is received +* ``humidity`` - Humidity value in percentage +* ``src_endpoint`` - Source endpoint that sent the humidity data +* ``src_address`` - Source address information + +onHumidityConfigReceive +^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function for receiving humidity sensor configuration data. + +.. code-block:: arduino + + void onHumidityConfigReceive(void (*callback)(float min_humidity, float max_humidity, float tolerance)); + +* ``callback`` - Function to call when humidity sensor configuration is received +* ``min_humidity`` - Minimum humidity supported by the sensor +* ``max_humidity`` - Maximum humidity supported by the sensor +* ``tolerance`` - Humidity tolerance of the sensor + Temperature Data Retrieval ************************** @@ -98,7 +139,7 @@ Requests temperature data from a specific group. * ``group_addr`` - Group address to send the request to getTemperature (Endpoint + Short Address) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Requests temperature data from a specific endpoint using short address. @@ -110,7 +151,7 @@ Requests temperature data from a specific endpoint using short address. * ``short_addr`` - Short address of the target device getTemperature (Endpoint + IEEE Address) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Requests temperature data from a specific endpoint using IEEE address. @@ -121,49 +162,143 @@ Requests temperature data from a specific endpoint using IEEE address. * ``endpoint`` - Target endpoint number * ``ieee_addr`` - IEEE address of the target device -Sensor Settings Retrieval -************************* +Humidity Data Retrieval +*********************** -getSensorSettings -^^^^^^^^^^^^^^^^^ +getHumidity +^^^^^^^^^^^ + +Requests humidity data from all bound sensors. + +.. code-block:: arduino + + void getHumidity(); + +getHumidity (Group) +^^^^^^^^^^^^^^^^^^^ + +Requests humidity data from a specific group. + +.. code-block:: arduino + + void getHumidity(uint16_t group_addr); + +* ``group_addr`` - Group address to send the request to + +getHumidity (Endpoint + Short Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Requests humidity data from a specific endpoint using short address. + +.. code-block:: arduino + + void getHumidity(uint8_t endpoint, uint16_t short_addr); + +* ``endpoint`` - Target endpoint number +* ``short_addr`` - Short address of the target device + +getHumidity (Endpoint + IEEE Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Requests humidity data from a specific endpoint using IEEE address. + +.. code-block:: arduino + + void getHumidity(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + +* ``endpoint`` - Target endpoint number +* ``ieee_addr`` - IEEE address of the target device + +Temperature Settings Retrieval +****************************** + +getTemperatureSettings +^^^^^^^^^^^^^^^^^^^^^^ + +Requests temperature sensor settings from all bound sensors. + +.. code-block:: arduino + + void getTemperatureSettings(); + +getTemperatureSettings (Group) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Requests temperature sensor settings from a specific group. -Requests sensor settings from all bound sensors. +.. code-block:: arduino + + void getTemperatureSettings(uint16_t group_addr); + +* ``group_addr`` - Group address to send the request to + +getTemperatureSettings (Endpoint + Short Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Requests temperature sensor settings from a specific endpoint using short address. .. code-block:: arduino - void getSensorSettings(); + void getTemperatureSettings(uint8_t endpoint, uint16_t short_addr); + +* ``endpoint`` - Target endpoint number +* ``short_addr`` - Short address of the target device -getSensorSettings (Group) -^^^^^^^^^^^^^^^^^^^^^^^^^ +getTemperatureSettings (Endpoint + IEEE Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Requests sensor settings from a specific group. +Requests temperature sensor settings from a specific endpoint using IEEE address. .. code-block:: arduino - void getSensorSettings(uint16_t group_addr); + void getTemperatureSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + +* ``endpoint`` - Target endpoint number +* ``ieee_addr`` - IEEE address of the target device + +Humidity Settings Retrieval +*************************** + +getHumiditySettings +^^^^^^^^^^^^^^^^^^^ + +Requests humidity sensor settings from all bound sensors. + +.. code-block:: arduino + + void getHumiditySettings(); + +getHumiditySettings (Group) +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Requests humidity sensor settings from a specific group. + +.. code-block:: arduino + + void getHumiditySettings(uint16_t group_addr); * ``group_addr`` - Group address to send the request to -getSensorSettings (Endpoint + Short Address) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +getHumiditySettings (Endpoint + Short Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Requests sensor settings from a specific endpoint using short address. +Requests humidity sensor settings from a specific endpoint using short address. .. code-block:: arduino - void getSensorSettings(uint8_t endpoint, uint16_t short_addr); + void getHumiditySettings(uint8_t endpoint, uint16_t short_addr); * ``endpoint`` - Target endpoint number * ``short_addr`` - Short address of the target device -getSensorSettings (Endpoint + IEEE Address) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +getHumiditySettings (Endpoint + IEEE Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Requests sensor settings from a specific endpoint using IEEE address. +Requests humidity sensor settings from a specific endpoint using IEEE address. .. code-block:: arduino - void getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + void getHumiditySettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); * ``endpoint`` - Target endpoint number * ``ieee_addr`` - IEEE address of the target device @@ -228,6 +363,66 @@ Configures temperature reporting for a specific endpoint using IEEE address. * ``max_interval`` - Maximum reporting interval in seconds * ``delta`` - Minimum change in temperature to trigger a report +Humidity Reporting Configuration +******************************** + +setHumidityReporting +^^^^^^^^^^^^^^^^^^^^ + +Configures humidity reporting for all bound sensors. + +.. code-block:: arduino + + void setHumidityReporting(uint16_t min_interval, uint16_t max_interval, float delta); + +* ``min_interval`` - Minimum reporting interval in seconds +* ``max_interval`` - Maximum reporting interval in seconds +* ``delta`` - Minimum change in humidity to trigger a report + +setHumidityReporting (Group) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Configures humidity reporting for a specific group. + +.. code-block:: arduino + + void setHumidityReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta); + +* ``group_addr`` - Group address to configure +* ``min_interval`` - Minimum reporting interval in seconds +* ``max_interval`` - Maximum reporting interval in seconds +* ``delta`` - Minimum change in humidity to trigger a report + +setHumidityReporting (Endpoint + Short Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Configures humidity reporting for a specific endpoint using short address. + +.. code-block:: arduino + + void setHumidityReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta); + +* ``endpoint`` - Target endpoint number +* ``short_addr`` - Short address of the target device +* ``min_interval`` - Minimum reporting interval in seconds +* ``max_interval`` - Maximum reporting interval in seconds +* ``delta`` - Minimum change in humidity to trigger a report + +setHumidityReporting (Endpoint + IEEE Address) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Configures humidity reporting for a specific endpoint using IEEE address. + +.. code-block:: arduino + + void setHumidityReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta); + +* ``endpoint`` - Target endpoint number +* ``ieee_addr`` - IEEE address of the target device +* ``min_interval`` - Minimum reporting interval in seconds +* ``max_interval`` - Maximum reporting interval in seconds +* ``delta`` - Minimum change in humidity to trigger a report + Example ------- diff --git a/docs/en/zigbee/ep_vibration_sensor.rst b/docs/en/zigbee/ep_vibration_sensor.rst index 896c4672c6d..d8ca3fdd5f4 100644 --- a/docs/en/zigbee/ep_vibration_sensor.rst +++ b/docs/en/zigbee/ep_vibration_sensor.rst @@ -73,9 +73,42 @@ Manually reports the current vibration state. .. code-block:: arduino - void report(); + bool report(); -This function does not return a value. +This function will return ``true`` if successful, ``false`` otherwise. + +requestIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Requests a new IAS Zone enrollment. Can be called to enroll a new device or to re-enroll an already enrolled device. + +.. code-block:: arduino + + bool requestIASZoneEnroll(); + +This function will return ``true`` if the enrollment request was sent successfully, ``false`` otherwise. The actual enrollment status should be checked using the ``enrolled()`` method after waiting for the enrollment response. + +restoreIASZoneEnroll +^^^^^^^^^^^^^^^^^^^^ + +Restores IAS Zone enrollment from stored attributes. This method should be called after rebooting an already enrolled device. It restores the enrollment information from flash memory, which is faster for sleepy devices compared to requesting a new enrollment. + +.. code-block:: arduino + + bool restoreIASZoneEnroll(); + +This function will return ``true`` if the enrollment was successfully restored, ``false`` otherwise. The enrollment information (zone ID and IAS CIE address) must be available in the device's stored attributes for this to succeed. + +enrolled +^^^^^^^^ + +Checks if the device is currently enrolled in the IAS Zone. + +.. code-block:: arduino + + bool enrolled(); + +This function returns ``true`` if the device is enrolled, ``false`` otherwise. Use this method to check the enrollment status after calling ``requestIASZoneEnroll()`` or ``restoreIASZoneEnroll()``. Example ------- diff --git a/docs/en/zigbee/zigbee_core.rst b/docs/en/zigbee/zigbee_core.rst index 89cf88ecca1..869af3007df 100644 --- a/docs/en/zigbee/zigbee_core.rst +++ b/docs/en/zigbee/zigbee_core.rst @@ -51,6 +51,24 @@ This function will return ``true`` if initialization successful, ``false`` other * Zigbee mode to ``Zigbee ED (end device)``. * Partition scheme to ``Zigbee xMB with spiffs`` (where ``x`` is the number of MB of selected flash size). +start +^^^^^ +Starts the Zigbee stack again, if it was stopped by calling ``stop()``. + +.. code-block:: arduino + + void start(); + + +stop +^^^^ +Stops the Zigbee stack. This can be used after calling ``begin()`` to stop the Zigbee stack. +Usage example is to save power or when you need the radio to be available for other tasks. + +.. code-block:: arduino + + void stop(); + Network Status ************** diff --git a/idf_component.yml b/idf_component.yml index f9357a401f9..3e376391af2 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,30 +1,35 @@ -description: "Arduino core for ESP32, ESP32-S and ESP32-C series of SoCs" +description: "Arduino core for ESP32, ESP32-C, ESP32-H, ESP32-P, ESP32-S series of SoCs" url: "https://github.com/espressif/arduino-esp32" license: "LGPL-2.1" targets: - esp32 - - esp32s2 - - esp32s3 - esp32c2 - esp32c3 + - esp32c5 - esp32c6 + - esp32c61 - esp32h2 - esp32p4 - - esp32c5 + - esp32s2 + - esp32s3 tags: - arduino files: include: - "variants/esp32/**/*" - - "variants/esp32s2/**/*" - - "variants/esp32s3/**/*" - "variants/esp32c2/**/*" - "variants/esp32c3/**/*" + - "variants/esp32c5/**/*" - "variants/esp32c6/**/*" + - "variants/esp32c61/**/*" - "variants/esp32h2/**/*" - "variants/esp32p4/**/*" - - "variants/esp32c5/**/*" + - "variants/esp32s2/**/*" + - "variants/esp32s3/**/*" exclude: + - ".*" # All files in the root directory that start with a dot. + - ".gitlab/" + - ".gitlab/**/*" - "docs/" - "docs/**/*" - "idf_component_examples/" @@ -36,9 +41,6 @@ files: - "tools/" - "tools/**/*" - "variants/**/*" - - ".gitignore" - - ".gitmodules" - - ".readthedocs.yaml" - "boards.txt" - "CODE_OF_CONDUCT.md" - "LICENSE.md" @@ -54,19 +56,19 @@ dependencies: espressif/esp_modem: version: "^1.1.0" espressif/esp-zboss-lib: - version: "==1.6.4" # compatible with esp-zigbee-lib 1.6.6 + version: "==1.6.4" # compatible with esp-zigbee-lib 1.6.8 require: public rules: - - if: "target not in [esp32c2, esp32p4]" + - if: "target not in [esp32c2, esp32c61, esp32p4]" espressif/esp-zigbee-lib: - version: "==1.6.6" + version: "==1.6.8" require: public rules: - - if: "target not in [esp32c2, esp32p4]" + - if: "target not in [esp32c2, esp32c61, esp32p4]" espressif/esp-dsp: version: "^1.3.4" rules: - - if: "target != esp32c2" + - if: "target not in [esp32c2, esp32c61]" # RainMaker Start (Fixed versions, because Matter supports only Insights 1.0.1) espressif/network_provisioning: version: "1.0.2" @@ -94,7 +96,7 @@ dependencies: rules: - if: "target not in [esp32c2, esp32p4]" espressif/cbor: - version: "0.6.0~1" + version: "0.6.1~4" rules: - if: "target not in [esp32c2, esp32p4]" espressif/qrcode: @@ -103,9 +105,9 @@ dependencies: - if: "target not in [esp32c2, esp32p4]" # RainMaker End espressif/esp-sr: - version: "^1.4.2" + version: "^2.1.5" rules: - - if: "target in [esp32s3]" + - if: "target in [esp32s3, esp32p4]" espressif/esp_hosted: version: "^2.0.12" rules: @@ -125,7 +127,11 @@ dependencies: chmorgan/esp-libhelix-mp3: version: "1.0.3" require: public + espressif/lan867x: + version: "^2.0.0" + rules: + - if: "target in [esp32, esp32p4]" examples: - path: ./idf_component_examples/hello_world - path: ./idf_component_examples/hw_cdc_hello_world - - path: ./idf_component_examples/esp_matter_light + - path: ./idf_component_examples/Arduino_ESP_Matter_over_OpenThread diff --git a/idf_component_examples/esp_matter_light/CMakeLists.txt b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/CMakeLists.txt similarity index 54% rename from idf_component_examples/esp_matter_light/CMakeLists.txt rename to idf_component_examples/Arduino_ESP_Matter_over_OpenThread/CMakeLists.txt index 1430df8ff78..282ad5bb137 100644 --- a/idf_component_examples/esp_matter_light/CMakeLists.txt +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/CMakeLists.txt @@ -2,24 +2,15 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.5) -set(PROJECT_VER "1.0") -set(PROJECT_VER_NUMBER 1) - -# This should be done before using the IDF_TARGET variable. include($ENV{IDF_PATH}/tools/cmake/project.cmake) -idf_build_set_property(MINIMAL_BUILD ON) -project(arduino_managed_component_light) - -# WARNING: This is just an example for using key for decrypting the encrypted OTA image -# Please do not use it as is. -if(CONFIG_ENABLE_ENCRYPTED_OTA) - target_add_binary_data(light.elf "esp_image_encryption_key.pem" TEXT) -endif() +#target_compile_options(espressif__esp_matter PUBLIC +# -DCHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER= +# -DCHIP_HAVE_CONFIG_H) +#list(APPEND compile_definitions "CHIP_HAVE_CONFIG_H=1") +#list(APPEND compile_definitions "CHIP_ADDRESS_RESOLVE_IMPL_INCLUDE_HEADER=") -if(CONFIG_IDF_TARGET_ESP32C2) - include(relinker) -endif() +project(Matter_Thread_Light) 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) diff --git a/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/README.md b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/README.md new file mode 100644 index 00000000000..4c88aef7199 --- /dev/null +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/README.md @@ -0,0 +1,38 @@ +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | -------- | + +# Arduino ESP-Matter over Thread example using ESP32-C5, ESP32-C6 and ESP32-H2 (any SoC with Thread radio) +This is an Arduino as IDF Project to build an ESP-Matter over Thread RGB Light using ESP32-C5/C6/H2 and ESP-Matter Arduino API \ +This example shall work with Arduino 3.3.2+ and also IDF 5.5.1+\ +It is necessary to make sure that the IDF version matches with the one used to release the Arduino Core version.\ +This can be done looking into release information in https://github.com/espressif/arduino-esp32/releases \ + +Any example from [ESP32 Matter Library examples](https://github.com/espressif/arduino-esp32/tree/master/libraries/Matter/examples) +can be used to build the application.\ +Feel free to create your own Arduino Matter sketch!\ +Do not forget to rename the `sketch_file_name.ino` to `sketch_file_name.cpp` in `main` folder. + +The `main/idf_component.yml` file holds the ESP-Matter component version and Arduino Core version.\ +Edit this file to set the target versions, if necessary. + +# General Instructions: + +1- Install the required IDF version into your computer. It can be done following the guide in +https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/get-started/index.html + +For Windows: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/get-started/index.html \ +For Linux or macOS: https://docs.espressif.com/projects/esp-idf/en/stable/esp32c6/get-started/linux-macos-setup.html + +2- Test IDF with `idf.py --version` to check if it is installed and configured correctly. + +3- To create a ESP-IDF project from this example with the latest release of Arduino-esp32, you can simply run command: +`idf.py create-project-from-example "espressif/arduino-esp32:Arduino_ESP_Matter_over_OpenThread"` +ESP-IDF will download all dependencies needed from the component registry and setup the project for you. + +4- Open an IDF terminal and execute `idf.py set-target esp32c6` (esp32c5 and esp32h2 are also possible targets) + +5- Execute `idf.py -p flash monitor` + +6- It will build, upload and show the UART0 output in the screen. + +7- Try to add the new Matter device to your local Matter environment. diff --git a/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/ci.yml b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/ci.yml new file mode 100644 index 00000000000..b826c20507e --- /dev/null +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/ci.yml @@ -0,0 +1,10 @@ +targets: + esp32s2: false + esp32s3: false + esp32c2: false + esp32c61: false + esp32p4: false +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y + - CONFIG_MBEDTLS_HKDF_C=y diff --git a/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/CMakeLists.txt b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/CMakeLists.txt new file mode 100644 index 00000000000..f18fa805695 --- /dev/null +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS ".") diff --git a/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/MatterEnhancedColorLight.cpp b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/MatterEnhancedColorLight.cpp new file mode 100644 index 00000000000..3f4735f0d22 --- /dev/null +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/MatterEnhancedColorLight.cpp @@ -0,0 +1,186 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Matter Manager +#include +#include + +// List of Matter Endpoints for this Node +// Color Light Endpoint +MatterEnhancedColorLight EnhancedColorLight; + +// It will use HSV color to control all Matter Attribute Changes +HsvColor_t currentHSVColor = {0, 0, 0}; + +// it will keep last OnOff & HSV Color state stored, using Preferences +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; +const char *hsvColorPrefKey = "HSV"; + +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t ledPin = RGB_BUILTIN; +#else +const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debounceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Set the RGB LED Light based on the current state of the Enhanced Color Light +bool setLightState(bool state, espHsvColor_t colorHSV, uint8_t brightness, uint16_t temperature_Mireds) { + + if (state) { +#ifdef RGB_BUILTIN + // currentHSVColor keeps final color result + espRgbColor_t rgbColor = espHsvColorToRgbColor(currentHSVColor); + // set the RGB LED + rgbLedWrite(ledPin, rgbColor.r, rgbColor.g, rgbColor.b); +#else + // No Color RGB LED, just use the HSV value (brightness) to control the LED + analogWrite(ledPin, colorHSV.v); +#endif + } else { +#ifndef RGB_BUILTIN + // after analogWrite(), it is necessary to set the GPIO to digital mode first + pinMode(ledPin, OUTPUT); +#endif + digitalWrite(ledPin, LOW); + } + // store last HSV Color and OnOff state for when the Light is restarted / power goes off + matterPref.putBool(onOffPrefKey, state); + matterPref.putUInt(hsvColorPrefKey, currentHSVColor.h << 16 | currentHSVColor.s << 8 | currentHSVColor.v); + // This callback must return the success state to Matter core + return true; +} + +void setup() { + // Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the LED (light) GPIO and Matter End Point + pinMode(ledPin, OUTPUT); + + Serial.begin(115200); + + // Initialize Matter EndPoint + matterPref.begin("MatterPrefs", false); + // default OnOff state is ON if not stored before + bool lastOnOffState = matterPref.getBool(onOffPrefKey, true); + // default HSV color is (21, 216, 25) - Warm White Color at 10% intensity + uint32_t prefHsvColor = matterPref.getUInt(hsvColorPrefKey, 21 << 16 | 216 << 8 | 25); + currentHSVColor = {uint8_t(prefHsvColor >> 16), uint8_t(prefHsvColor >> 8), uint8_t(prefHsvColor)}; + EnhancedColorLight.begin(lastOnOffState, currentHSVColor); + // set the callback function to handle the Light state change + EnhancedColorLight.onChange(setLightState); + + // lambda functions are used to set the attribute change callbacks + EnhancedColorLight.onChangeOnOff([](bool state) { + Serial.printf("Light OnOff changed to %s\r\n", state ? "ON" : "OFF"); + return true; + }); + EnhancedColorLight.onChangeColorTemperature([](uint16_t colorTemperature) { + Serial.printf("Light Color Temperature changed to %d\r\n", colorTemperature); + // get correspondent Hue and Saturation of the color temperature + HsvColor_t hsvTemperature = espRgbColorToHsvColor(espCTToRgbColor(colorTemperature)); + // keep previous the brightness and just change the Hue and Saturation + currentHSVColor.h = hsvTemperature.h; + currentHSVColor.s = hsvTemperature.s; + return true; + }); + EnhancedColorLight.onChangeBrightness([](uint8_t brightness) { + Serial.printf("Light brightness changed to %d\r\n", brightness); + // change current brightness (HSV value) + currentHSVColor.v = brightness; + return true; + }); + EnhancedColorLight.onChangeColorHSV([](HsvColor_t hsvColor) { + Serial.printf("Light HSV Color changed to (%d,%d,%d)\r\n", hsvColor.h, hsvColor.s, hsvColor.v); + // keep the current brightness and just change Hue and Saturation + currentHSVColor.h = hsvColor.h; + currentHSVColor.s = hsvColor.s; + return true; + }); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + // This may be a restart of a already commissioned Matter accessory + if (Matter.isDeviceCommissioned()) { + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + Serial.printf( + "Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r, + EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b + ); + // configure the Light based on initial on-off state and its color + EnhancedColorLight.updateAccessory(); + } +} + +void loop() { + // Check Matter Light Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Light Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.printf( + "Initial state: %s | RGB Color: (%d,%d,%d) \r\n", EnhancedColorLight ? "ON" : "OFF", EnhancedColorLight.getColorRGB().r, + EnhancedColorLight.getColorRGB().g, EnhancedColorLight.getColorRGB().b + ); + // configure the Light based on initial on-off state and its color + EnhancedColorLight.updateAccessory(); + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } + + // A button is also used to control the light + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + // Onboard User Button is used as a Light toggle switch or to decommission it + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > debounceTime && digitalRead(buttonPin) == HIGH) { + button_state = false; // released + // Toggle button is released - toggle the light + Serial.println("User button released. Toggling Light!"); + EnhancedColorLight.toggle(); // Matter Controller also can see the change + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again."); + EnhancedColorLight = false; // turn the light off + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so + } +} diff --git a/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/idf_component.yml b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/idf_component.yml new file mode 100644 index 00000000000..233b595e654 --- /dev/null +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + espressif/esp_matter: + version: ">=1.3.0" + require: public + espressif/arduino-esp32: + version: ">=3.1.0" + override_path: "../../../" + pre_release: true diff --git a/idf_component_examples/esp_matter_light/partitions.csv b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/partitions.csv similarity index 100% rename from idf_component_examples/esp_matter_light/partitions.csv rename to idf_component_examples/Arduino_ESP_Matter_over_OpenThread/partitions.csv diff --git a/idf_component_examples/esp_matter_light/sdkconfig.defaults.c6_thread b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/sdkconfig.defaults similarity index 57% rename from idf_component_examples/esp_matter_light/sdkconfig.defaults.c6_thread rename to idf_component_examples/Arduino_ESP_Matter_over_OpenThread/sdkconfig.defaults index 502480f94b1..aa3d5c838eb 100644 --- a/idf_component_examples/esp_matter_light/sdkconfig.defaults.c6_thread +++ b/idf_component_examples/Arduino_ESP_Matter_over_OpenThread/sdkconfig.defaults @@ -1,24 +1,31 @@ -CONFIG_IDF_TARGET="esp32c6" - -# Arduino Settings -CONFIG_FREERTOS_HZ=1000 -CONFIG_AUTOSTART_ARDUINO=y - -# Log Levels -# Boot Messages - Log level +# Arduino ESP32 settings CONFIG_BOOTLOADER_LOG_LEVEL_ERROR=y -# Arduino Log Level +CONFIG_AUTOSTART_ARDUINO=y CONFIG_ARDUHAL_LOG_DEFAULT_LEVEL_INFO=y -# IDF Log Level +CONFIG_FREERTOS_HZ=1000 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 +# Enables Arduino Selective Library Compilation: Arduino Matter + Preferences Library +CONFIG_ARDUINO_SELECTIVE_COMPILATION=y +CONFIG_ARDUINO_SELECTIVE_Preferences=y +CONFIG_ARDUINO_SELECTIVE_Network=y +CONFIG_ARDUINO_SELECTIVE_ESPmDNS=y +CONFIG_ARDUINO_SELECTIVE_Matter=y + +# Flash Configuration +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHFREQ="80m" CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" +CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y +CONFIG_ESPTOOLPY_BEFORE_RESET=y +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # libsodium CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y @@ -30,9 +37,6 @@ CONFIG_BT_NIMBLE_EXT_ADV=n CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=n -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - # Enable OpenThread CONFIG_OPENTHREAD_ENABLED=y CONFIG_OPENTHREAD_SRP_CLIENT=y @@ -56,24 +60,9 @@ CONFIG_LWIP_MULTICAST_PING=y CONFIG_USE_MINIMAL_MDNS=n CONFIG_ENABLE_EXTENDED_DISCOVERY=y -# Enable OTA Requester -CONFIG_ENABLE_OTA_REQUESTOR=n - -# Disable STA and AP for ESP32C6 +# Disable STA and AP for ESP32C6 ==> no Wifi, thread only CONFIG_ENABLE_WIFI_STATION=n CONFIG_ENABLE_WIFI_AP=n -# Enable chip shell -CONFIG_ENABLE_CHIP_SHELL=n - -# Disable persist subscriptions -CONFIG_ENABLE_PERSIST_SUBSCRIPTIONS=n - -# MRP configs -CONFIG_MRP_LOCAL_ACTIVE_RETRY_INTERVAL_FOR_THREAD=5000 -CONFIG_MRP_LOCAL_IDLE_RETRY_INTERVAL_FOR_THREAD=5000 -CONFIG_MRP_RETRY_INTERVAL_SENDER_BOOST_FOR_THREAD=5000 -CONFIG_MRP_MAX_RETRANS=3 - # Enable HKDF in mbedtls CONFIG_MBEDTLS_HKDF_C=y diff --git a/idf_component_examples/esp_matter_light/README.md b/idf_component_examples/esp_matter_light/README.md deleted file mode 100644 index b0173f6a437..00000000000 --- a/idf_component_examples/esp_matter_light/README.md +++ /dev/null @@ -1,124 +0,0 @@ -| Supported Targets | ESP32-S3 | ESP32-C3 | ESP32-C6 | -| ----------------- | -------- | -------- | -------- | - - -# Managed Component Light - -This example sets automatically the RGB LED GPIO and BOOT Button GPIO based on the default pin used by the selected Devkit Board. - -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. - -Read the [documentation](https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html) for more information about building and flashing the firmware. - -The code is based on the Arduino API and uses Arduino as an IDF Component. - -## How to use it - -Once the device runs for the first time, it must be commissioned to the Matter Fabric of the available Matter Environment. -Possible Matter Environments are: -- Amazon Alexa -- Google Home Assistant (*) -- Apple Home -- Open Source Home Assistant - -(*) Google Home Assistant requires the user to set up a Matter Light using the [Google Home Developer Console](https://developers.home.google.com/codelabs/matter-device#2). It is necessary to create a Matter Light device with VID = 0xFFF1 and PID = 0x8000. Otherwise, the Light won't show up in the GHA APP. This action is necessary because the Firmware uses Testing credentials and Google requires the user to create the testing device before using it. - -**There is no QR Code** to be used when the Smartphone APP wants to add the Matter Device. -Please enter the code manually: `34970112332` - -Each Devkit Board has a built-in LED that will be used as the Matter Light. -The default setting for ESP32-S3 is pin 48, for ESP32-C3 and ESP32-C6, it is pin 8. -The BOOT Button pin of ESP32-S3 is GPIO 0, by toher hand, the ESP32-C3 and ESP32-C6 use GPIO 9. -Please change it in using the MenuConfig executing `idf.py menuconfig` and selecting `Menu->Light Matter Accessory` options. - -## LED Status and Factory Mode - -The WS2812b built-in LED will turn purple as soon as the device is flashed and runs for the first time. -The purple color indicates that the Matter Accessory has not been commissioned yet. -After using a Matter provider Smartphone APP to add a Matter device to your Home Application, it may turn orange to indicate that it has no Wi-Fi connection. - -Once it connects to the Wi-Fi network, the LED will turn white to indicate that Matter is working and the device is connected to the Matter Environment. -Please note that Matter over Wi-Fi using an ESP32 device will connect to a 2.4 GHz Wi-Fi SSID, therefore the Commissioner APP Smartphone shall be connected to this SSID. - -The Matter and Wi-Fi configuration will be stored in NVS to ensure that it will connect to the Matter Fabric and Wi-Fi Network again once it is reset. - -The Matter Smartphone APP will control the light state (ON/OFF), temperature (Warm/Cold White), and brightness. - -## On Board Light toggle button - -The built-in BOOT button will toggle On/Off and replicate the new state to the Matter Environment, making it visible in the Matter Smartphone APP as well. - -## Returning to the Factory State - -Holding the BOOT button pressed for more than 10 seconds and then releasing it will erase all Matter and Wi-Fi configuration, forcing it to reset to factory state. After that, the device needs to be commissioned again. -Previous setups done in the Smartphone APP won't work again; therefore, the virtual device shall be removed from the APP. - -## Building the Application using Wi-Fi 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 - -The project will download all necessary components, including the Arduino Core. -Execute this sequence: - ` using linux rm command or Windows rmdir command` - `idf.py set-target ` - `idf.py -D SDKCONFIG_DEFAULTS="sdkconfig_file1;sdkconfig_file2;sdkconfig_fileX" -p flash monitor` - -Example for ESP32-S3/Linux | macOS: -``` -rm -rf build -idf.py set-target esp32s3 -idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults" -p /dev/ttyACM0 flash monitor -``` -Example for ESP32-C3/Windows: -``` -rmdir /s/q build -idf.py set-target esp32c3 -idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults" -p com3 flash monitor -``` - -It may be necessary to delete some folders and files before running `idf.py` -- Linux/macOS: - ``` - rm -rf build managed_components sdkconfig dependencies.lock - ``` -- Windows: - ``` - rmdir /s/q build managed_components && del sdkconfig dependencies.lock - ``` - -There is a configuration file for these SoC: esp32s3, esp32c3, esp32c6. -Those are the tested devices that have a WS2812 RGB LED and can run BLE, Wi-Fi and Matter. - -In case it is necessary to change the Button Pin or the REG LED Pin, please use the `menuconfig` -`idf.py menuconfig` and change the Menu Option `Light Matter Accessory` - -## Building the Application using OpenThread and Matter - -This is possible with the ESP32-C6. -It is necessary to have a Thread Border Router in the Matter Environment. -Check your Matter hardware provider. -In order to build the application that will use Thread Networking instead of Wi-Fi, please execute: - -Example for ESP32-C6/Linux | macOS: -``` -rm -rf build -idf.py set-target esp32c6 -idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.c6_thread" -p /dev/ttyACM0 flash monitor -``` -Example for ESP32-C6/Windows: -``` -rmdir /s/q build -idf.py set-targt esp32c6 -idf.py -D SDKCONFIG_DEFAULTS="sdkconfig.defaults;sdkconfig.defaults.c6_thread" -p com3 flash monitor -``` - -It may be necessary to delete some folders and files before running `idf.py` -- Linux/macOS - ``` - rm -rf build managed_components sdkconfig dependencies.lock - ``` -- Windows - ``` - rmdir /s/q build managed_components && del sdkconfig dependencies.lock - ``` diff --git a/idf_component_examples/esp_matter_light/ci.json b/idf_component_examples/esp_matter_light/ci.json deleted file mode 100644 index f23a085285d..00000000000 --- a/idf_component_examples/esp_matter_light/ci.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "targets": { - "esp32c2": false, - "esp32s2": false - }, - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y", - "CONFIG_MBEDTLS_HKDF_C=y" - ] -} diff --git a/idf_component_examples/esp_matter_light/main/CMakeLists.txt b/idf_component_examples/esp_matter_light/main/CMakeLists.txt deleted file mode 100644 index 6b91a8cf510..00000000000 --- a/idf_component_examples/esp_matter_light/main/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register(SRC_DIRS "." - INCLUDE_DIRS ".") - -set_property(TARGET ${COMPONENT_LIB} PROPERTY CXX_STANDARD 17) -target_compile_options(${COMPONENT_LIB} PRIVATE "-DCHIP_HAVE_CONFIG_H") diff --git a/idf_component_examples/esp_matter_light/main/Kconfig.projbuild b/idf_component_examples/esp_matter_light/main/Kconfig.projbuild deleted file mode 100644 index 3e0a35c5e15..00000000000 --- a/idf_component_examples/esp_matter_light/main/Kconfig.projbuild +++ /dev/null @@ -1,42 +0,0 @@ -menu "Light Matter Accessory" - menu "On Board Light ON/OFF Button" - config BUTTON_PIN - int - prompt "Button 1 GPIO" - 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 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 - - config ENV_GPIO_RANGE_MIN - int - default 0 - - 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_OUT_RANGE_MAX - int - default ENV_GPIO_RANGE_MAX - -endmenu diff --git a/idf_component_examples/esp_matter_light/main/builtinLED.cpp b/idf_component_examples/esp_matter_light/main/builtinLED.cpp deleted file mode 100644 index 8795dde2756..00000000000 --- a/idf_component_examples/esp_matter_light/main/builtinLED.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. - This will implement the onboard WS2812b LED as a LED indicator - It can be used to indicate some state or status of the device - The LED can be controlled using RGB, HSV or color temperature, brightness - - In this example, the LED Indicator class is used as the Matter light accessory -*/ - -#include "builtinLED.h" - -typedef struct { - 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} -}; - -/* 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, -}; - -BuiltInLED::BuiltInLED() { - pin_number = (uint8_t)-1; // no pin number - state = false; // LED is off - hsv_color.value = 0; // black color -} - -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; - - 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; - } 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); - } - } - 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; - - 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) { - case 0: - 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; - case 2: - 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; - case 4: - 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; - } - - // 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); - } 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 { - rgbLedWrite(pin_number, 0, 0, 0); - } - return true; - } else { - log_e("Invalid pin (%d) number", pin_number); - return false; - } -} - -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); -} diff --git a/idf_component_examples/esp_matter_light/main/builtinLED.h b/idf_component_examples/esp_matter_light/main/builtinLED.h deleted file mode 100644 index 1ca8c935569..00000000000 --- a/idf_component_examples/esp_matter_light/main/builtinLED.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. - This will implement the onboard WS2812b LED as a LED indicator - It can be used to indicate some state or status of the device - The LED can be controlled using RGB, HSV or color temperature, brightness - - In this example, the BuiltInLED class is used as the Matter light accessory -*/ - -#pragma once - -#include - -#define MAX_HUE 360 -#define MAX_SATURATION 255 -#define MAX_BRIGHTNESS 255 -#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. */ - }; -} 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. */ - }; -} led_indicator_color_rgb_t; - -class BuiltInLED { -private: - uint8_t pin_number; - bool state; - led_indicator_color_hsv_t hsv_color; - -public: - 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); - - void begin(uint8_t pin); - void end(); - - void on(); - void off(); - void toggle(); - bool getState(); - - 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); -}; diff --git a/idf_component_examples/esp_matter_light/main/idf_component.yml b/idf_component_examples/esp_matter_light/main/idf_component.yml deleted file mode 100644 index e0286324591..00000000000 --- a/idf_component_examples/esp_matter_light/main/idf_component.yml +++ /dev/null @@ -1,12 +0,0 @@ -dependencies: - espressif/esp_matter: - version: ">=1.3.0" - # Adds Arduino Core from GitHub repository using main branch - espressif/arduino-esp32: - version: ">=3.0.5" - override_path: "../../../" - pre_release: true - - # testing - using Arduino from the repository - # version: "master" # branch or commit - # git: https://github.com/espressif/arduino-esp32.git diff --git a/idf_component_examples/esp_matter_light/main/matter_accessory_driver.cpp b/idf_component_examples/esp_matter_light/main/matter_accessory_driver.cpp deleted file mode 100644 index 523c38e6855..00000000000 --- a/idf_component_examples/esp_matter_light/main/matter_accessory_driver.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include -#include -#include "builtinLED.h" -#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_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; -} - -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_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; - - const uint8_t pin = WS2812_PIN; // set your board WS2812b pin here - builtinLED.begin(pin); - return (app_driver_handle_t)&builtinLED; -} diff --git a/idf_component_examples/esp_matter_light/main/matter_accessory_driver.h b/idf_component_examples/esp_matter_light/main/matter_accessory_driver.h deleted file mode 100644 index 3bf6655ab16..00000000000 --- a/idf_component_examples/esp_matter_light/main/matter_accessory_driver.h +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -// 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 -#else -#define WS2812_PIN CONFIG_WS2812_PIN // From sdkconfig.defaults. -#endif - -#ifndef RGB_BUILTIN -#define RGB_BUILTIN WS2812_PIN -#endif - -// 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 -#else -#define BUTTON_PIN CONFIG_BUTTON_PIN // From sdkconfig.defaults. -#endif - -/** Standard max values (used for remapping attributes) */ -#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_TEMPERATURE_FACTOR 1000000 - -/** Default attribute values used during initialization */ -#define DEFAULT_POWER true -#define DEFAULT_BRIGHTNESS 64 -#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_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); -esp_err_t light_accessory_set_temperature(void *led, uint16_t val); -app_driver_handle_t light_accessory_init(); diff --git a/idf_component_examples/esp_matter_light/main/matter_light.cpp b/idf_component_examples/esp_matter_light/main/matter_light.cpp deleted file mode 100644 index 6079ce46add..00000000000 --- a/idf_component_examples/esp_matter_light/main/matter_light.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - This example code is in the Public Domain (or CC0 licensed, at your option.) - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include "matter_accessory_driver.h" - -#include - -#include -#include -#include - -#include -#include - -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD -#include -#include "esp_openthread_types.h" - -#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_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 - -uint16_t light_endpoint_id = 0; - -using namespace esp_matter; -using namespace esp_matter::attribute; -using namespace esp_matter::endpoint; -using namespace chip::app::Clusters; - -constexpr auto k_timeout_seconds = 300; - -#if CONFIG_ENABLE_ENCRYPTED_OTA -extern const char decryption_key_start[] asm("_binary_esp_image_encryption_key_pem_start"); -extern const char decryption_key_end[] asm("_binary_esp_image_encryption_key_pem_end"); - -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 - -bool isAccessoryCommissioned() { - return chip::Server::GetInstance().GetFabricTable().FabricCount() > 0; -} - -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION -bool isWifiConnected() { - return chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected(); -} -#endif - -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD -bool isThreadConnected() { - return chip::DeviceLayer::ConnectivityMgr().IsThreadAttached(); -} -#endif - -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; - - 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::kCommissioningSessionStarted: log_i("Commissioning session started"); 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::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 - * 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; - } - - 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"); - } - - /* 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; - - 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; -} - -// 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; -} - -void setup() { - esp_err_t err = ESP_OK; - - /* 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; - - // 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; - - // 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); - - /* 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); // 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); -#endif - - /* 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 - -#if CONFIG_ENABLE_CHIP_SHELL - esp_matter::console::diagnostics_register_commands(); - esp_matter::console::wifi_register_commands(); -#if CONFIG_OPENTHREAD_CLI - esp_matter::console::otcli_register_commands(); -#endif - esp_matter::console::init(); -#endif -} - -void loop() { - static uint32_t button_time_stamp = 0; - static bool button_state = false; - static bool started = false; - - 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 -#endif - delay(5000); - return; - } - -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION - 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 -#endif - delay(5000); - return; - } -#endif - -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD - 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 -#endif - delay(5000); - return; - } -#endif - - // Once all network connections are established, the accessory is ready for use - // Run it only once - if (!started) { - log_i("Accessory is commissioned and connected to Wi-Fi. Ready for use."); - started = true; - // Starting driver with default values - matter_light_set_defaults(light_endpoint_id); - } - - // 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. - - // Toggle button is pressed - toggle the light - log_i("Toggle button pressed"); - - endpoint_t *endpoint = endpoint::get(node::get(), light_endpoint_id); - cluster_t *cluster = cluster::get(endpoint, OnOff::Id); - attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); - - esp_matter_attr_val_t val = esp_matter_invalid(NULL); - attribute::get_val(attribute, &val); - val.val.b = !val.val.b; - attribute::update(light_endpoint_id, OnOff::Id, OnOff::Attributes::OnOff::Id, &val); - } - - // 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. - - // 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 restored to factory settings."); - esp_matter::factory_reset(); - } - } - - delay(50); // WDT is happier with a delay -} diff --git a/idf_component_examples/esp_matter_light/sdkconfig.defaults b/idf_component_examples/esp_matter_light/sdkconfig.defaults deleted file mode 100644 index 8688318fa36..00000000000 --- a/idf_component_examples/esp_matter_light/sdkconfig.defaults +++ /dev/null @@ -1,67 +0,0 @@ -# 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 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 - -# 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 - -# -# DIAGNOSTICS -# -CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y diff --git a/idf_component_examples/esp_matter_light/sdkconfig.defaults.esp32c6 b/idf_component_examples/esp_matter_light/sdkconfig.defaults.esp32c6 deleted file mode 100644 index 9fe589613ef..00000000000 --- a/idf_component_examples/esp_matter_light/sdkconfig.defaults.esp32c6 +++ /dev/null @@ -1,16 +0,0 @@ -CONFIG_IDF_TARGET="esp32c6" - -# libsodium -CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y - -# NIMBLE -CONFIG_BT_NIMBLE_EXT_ADV=n -CONFIG_BT_NIMBLE_HCI_EVT_BUF_SIZE=70 -CONFIG_USE_BLE_ONLY_FOR_COMMISSIONING=y - -# FreeRTOS should use legacy API -CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY=y - -# Use minimal mDNS -CONFIG_USE_MINIMAL_MDNS=y -CONFIG_ENABLE_EXTENDED_DISCOVERY=y diff --git a/idf_component_examples/hw_cdc_hello_world/ci.json b/idf_component_examples/hw_cdc_hello_world/ci.json deleted file mode 100644 index 80669afc2cc..00000000000 --- a/idf_component_examples/hw_cdc_hello_world/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y" - ] -} diff --git a/idf_component_examples/hw_cdc_hello_world/ci.yml b/idf_component_examples/hw_cdc_hello_world/ci.yml new file mode 100644 index 00000000000..7415bc09cc4 --- /dev/null +++ b/idf_component_examples/hw_cdc_hello_world/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y diff --git a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino index b3b01be61cd..934789a52bf 100644 --- a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino +++ b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino @@ -19,6 +19,7 @@ const char *ssid = ".........."; const char *password = ".........."; +uint32_t last_ota_time = 0; void setup() { Serial.begin(115200); @@ -40,9 +41,13 @@ void setup() { // No authentication by default // ArduinoOTA.setPassword("admin"); - // Password can be set with it's md5 value as well - // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 - // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + // Password can be set with plain text (will be hashed internally) + // The authentication uses PBKDF2-HMAC-SHA256 with 10,000 iterations + // ArduinoOTA.setPassword("admin"); + + // Or set password with pre-hashed value (SHA256 hash of "admin") + // SHA256(admin) = 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 + // ArduinoOTA.setPasswordHash("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"); ArduinoOTA .onStart([]() { @@ -60,7 +65,10 @@ void setup() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { - Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + if (millis() - last_ota_time > 500) { + Serial.printf("Progress: %u%%\n", (progress / (total / 100))); + last_ota_time = millis(); + } }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); diff --git a/libraries/ArduinoOTA/examples/BasicOTA/ci.json b/libraries/ArduinoOTA/examples/BasicOTA/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/ArduinoOTA/examples/BasicOTA/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/ArduinoOTA/examples/BasicOTA/ci.yml b/libraries/ArduinoOTA/examples/BasicOTA/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/ArduinoOTA/examples/BasicOTA/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ArduinoOTA/library.properties b/libraries/ArduinoOTA/library.properties index 3a3f7e111e0..604e21d2819 100644 --- a/libraries/ArduinoOTA/library.properties +++ b/libraries/ArduinoOTA/library.properties @@ -1,5 +1,5 @@ name=ArduinoOTA -version=3.3.0 +version=3.3.4 author=Ivan Grokhotkov and Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables Over The Air upgrades, via wifi and espota.py UDP request/TCP download. diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.cpp b/libraries/ArduinoOTA/src/ArduinoOTA.cpp index cb3ddc1e797..a5b0d09de58 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/src/ArduinoOTA.cpp @@ -19,7 +19,9 @@ #include "ArduinoOTA.h" #include "NetworkClient.h" #include "ESPmDNS.h" -#include "MD5Builder.h" +#include "HEXBuilder.h" +#include "SHA2Builder.h" +#include "PBKDF2_HMACBuilder.h" #include "Update.h" // #define OTA_DEBUG Serial @@ -72,18 +74,40 @@ String ArduinoOTAClass::getHostname() { ArduinoOTAClass &ArduinoOTAClass::setPassword(const char *password) { if (_state == OTA_IDLE && password) { - MD5Builder passmd5; - passmd5.begin(); - passmd5.add(password); - passmd5.calculate(); + // Hash the password with SHA256 for storage (not plain text) + SHA256Builder pass_hash; + pass_hash.begin(); + pass_hash.add(password); + pass_hash.calculate(); _password.clear(); - _password = passmd5.toString(); + _password = pass_hash.toString(); } return *this; } ArduinoOTAClass &ArduinoOTAClass::setPasswordHash(const char *password) { if (_state == OTA_IDLE && password) { + size_t len = strlen(password); + bool is_hex = HEXBuilder::isHexString(password, len); + + if (!is_hex) { + log_e("Invalid password hash. Expected hex string (0-9, a-f, A-F)."); + return *this; + } + + if (len == 32) { + // Warn if MD5 hash is detected (32 hex characters) + log_w("MD5 password hash detected. MD5 is deprecated and insecure."); + log_w("Please use setPassword() with plain text or setPasswordHash() with SHA256 hash (64 chars)."); + log_w("To generate SHA256: echo -n 'yourpassword' | sha256sum"); + } else if (len == 64) { + log_i("Using SHA256 password hash."); + } else { + log_e("Invalid password hash length. Expected 32 (deprecated MD5) or 64 (SHA256) characters."); + return *this; + } + + // Store the pre-hashed password directly _password.clear(); _password = password; } @@ -179,7 +203,7 @@ String ArduinoOTAClass::readStringUntil(char end) { void ArduinoOTAClass::_onRx() { if (_state == OTA_IDLE) { int cmd = parseInt(); - if (cmd != U_FLASH && cmd != U_SPIFFS) { + if (cmd != U_FLASH && cmd != U_FLASHFS) { return; } _cmd = cmd; @@ -188,17 +212,18 @@ void ArduinoOTAClass::_onRx() { _udp_ota.read(); _md5 = readStringUntil('\n'); _md5.trim(); - if (_md5.length() != 32) { + if (_md5.length() != 32) { // MD5 produces 32 character hex string for firmware integrity log_e("bad md5 length"); return; } if (_password.length()) { - MD5Builder nonce_md5; - nonce_md5.begin(); - nonce_md5.add(String(micros())); - nonce_md5.calculate(); - _nonce = nonce_md5.toString(); + // Generate a random challenge (nonce) + SHA256Builder nonce_sha256; + nonce_sha256.begin(); + nonce_sha256.add(String(micros()) + String(random(1000000))); + nonce_sha256.calculate(); + _nonce = nonce_sha256.toString(); _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); _udp_ota.printf("AUTH %s", _nonce.c_str()); @@ -222,20 +247,37 @@ void ArduinoOTAClass::_onRx() { _udp_ota.read(); String cnonce = readStringUntil(' '); String response = readStringUntil('\n'); - if (cnonce.length() != 32 || response.length() != 32) { + if (cnonce.length() != 64 || response.length() != 64) { // SHA256 produces 64 character hex string log_e("auth param fail"); _state = OTA_IDLE; return; } - String challenge = _password + ":" + String(_nonce) + ":" + cnonce; - MD5Builder _challengemd5; - _challengemd5.begin(); - _challengemd5.add(challenge); - _challengemd5.calculate(); - String result = _challengemd5.toString(); - - if (result.equals(response)) { + // Verify the challenge/response using PBKDF2-HMAC-SHA256 + // The client should derive a key using PBKDF2-HMAC-SHA256 with: + // - password: the OTA password (or its hash if using setPasswordHash) + // - salt: nonce + cnonce + // - iterations: 10000 (or configurable) + // Then hash the challenge with the derived key + + String salt = _nonce + ":" + cnonce; + SHA256Builder sha256; + // Use the stored password hash for PBKDF2 derivation + PBKDF2_HMACBuilder pbkdf2(&sha256, _password, salt, 10000); + + pbkdf2.begin(); + pbkdf2.calculate(); + String derived_key = pbkdf2.toString(); + + // Create challenge: derived_key + nonce + cnonce + String challenge = derived_key + ":" + _nonce + ":" + cnonce; + SHA256Builder challenge_sha256; + challenge_sha256.begin(); + challenge_sha256.add(challenge); + challenge_sha256.calculate(); + String expected_response = challenge_sha256.toString(); + + if (expected_response.equals(response)) { _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); _udp_ota.print("OK"); _udp_ota.endPacket(); @@ -266,7 +308,8 @@ void ArduinoOTAClass::_runUpdate() { _state = OTA_IDLE; return; } - Update.setMD5(_md5.c_str()); + + Update.setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication if (_start_callback) { _start_callback(); diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.h b/libraries/ArduinoOTA/src/ArduinoOTA.h index 7916e3b328d..a946388c4aa 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.h +++ b/libraries/ArduinoOTA/src/ArduinoOTA.h @@ -54,7 +54,7 @@ class ArduinoOTAClass { //Sets the password that will be required for OTA. Default NULL ArduinoOTAClass &setPassword(const char *password); - //Sets the password as above but in the form MD5(password). Default NULL + //Sets the password as above but in the form SHA256(password). Default NULL ArduinoOTAClass &setPasswordHash(const char *password); //Sets the partition label to write to when updating SPIFFS. Default NULL diff --git a/libraries/AsyncUDP/examples/AsyncUDPClient/ci.json b/libraries/AsyncUDP/examples/AsyncUDPClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/AsyncUDP/examples/AsyncUDPClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/AsyncUDP/examples/AsyncUDPClient/ci.yml b/libraries/AsyncUDP/examples/AsyncUDPClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/AsyncUDP/examples/AsyncUDPClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.json b/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.yml b/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/AsyncUDP/examples/AsyncUDPMulticastServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/AsyncUDP/examples/AsyncUDPServer/ci.json b/libraries/AsyncUDP/examples/AsyncUDPServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/AsyncUDP/examples/AsyncUDPServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/AsyncUDP/examples/AsyncUDPServer/ci.yml b/libraries/AsyncUDP/examples/AsyncUDPServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/AsyncUDP/examples/AsyncUDPServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/AsyncUDP/library.properties b/libraries/AsyncUDP/library.properties index ddf9f79b6d9..0dc51eae1f7 100644 --- a/libraries/AsyncUDP/library.properties +++ b/libraries/AsyncUDP/library.properties @@ -1,5 +1,5 @@ name=ESP32 Async UDP -version=3.3.0 +version=3.3.4 author=Me-No-Dev maintainer=Me-No-Dev sentence=Async UDP Library for ESP32 diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp index 2d533831cd5..ee4bcc86c30 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.cpp +++ b/libraries/AsyncUDP/src/AsyncUDP.cpp @@ -15,6 +15,27 @@ extern "C" { #include "lwip/priv/tcpip_priv.h" +#ifndef CONFIG_ARDUINO_UDP_TASK_STACK_SIZE +#define CONFIG_ARDUINO_UDP_TASK_STACK_SIZE 4096 +#endif +#ifndef ARDUINO_UDP_TASK_STACK_SIZE +#define ARDUINO_UDP_TASK_STACK_SIZE CONFIG_ARDUINO_UDP_TASK_STACK_SIZE +#endif + +#ifndef CONFIG_ARDUINO_UDP_TASK_PRIORITY +#define CONFIG_ARDUINO_UDP_TASK_PRIORITY 3 +#endif +#ifndef ARDUINO_UDP_TASK_PRIORITY +#define ARDUINO_UDP_TASK_PRIORITY CONFIG_ARDUINO_UDP_TASK_PRIORITY +#endif + +#ifndef CONFIG_ARDUINO_UDP_RUNNING_CORE +#define CONFIG_ARDUINO_UDP_RUNNING_CORE -1 +#endif +#ifndef ARDUINO_UDP_RUNNING_CORE +#define ARDUINO_UDP_RUNNING_CORE CONFIG_ARDUINO_UDP_RUNNING_CORE +#endif + #ifdef CONFIG_LWIP_TCPIP_CORE_LOCKING #define UDP_MUTEX_LOCK() \ if (!sys_thread_tcpip(LWIP_CORE_LOCK_QUERY_HOLDER)) { \ @@ -164,6 +185,7 @@ static QueueHandle_t _udp_queue; static volatile TaskHandle_t _udp_task_handle = NULL; static void _udp_task(void *pvParameters) { + (void)pvParameters; lwip_event_packet_t *e = NULL; for (;;) { if (xQueueReceive(_udp_queue, &e, portMAX_DELAY) == pdTRUE) { @@ -188,7 +210,7 @@ static bool _udp_task_start() { } if (!_udp_task_handle) { xTaskCreateUniversal( - _udp_task, "async_udp", 4096, NULL, CONFIG_ARDUINO_UDP_TASK_PRIORITY, (TaskHandle_t *)&_udp_task_handle, CONFIG_ARDUINO_UDP_RUNNING_CORE + _udp_task, "async_udp", ARDUINO_UDP_TASK_STACK_SIZE, NULL, ARDUINO_UDP_TASK_PRIORITY, (TaskHandle_t *)&_udp_task_handle, ARDUINO_UDP_RUNNING_CORE ); if (!_udp_task_handle) { return false; @@ -317,6 +339,33 @@ AsyncUDPPacket::AsyncUDPPacket(AsyncUDPPacket &packet) { pbuf_ref(_pb); } +AsyncUDPPacket &AsyncUDPPacket::operator=(const AsyncUDPPacket &packet) { + if (this != &packet) { + if (_pb) { + // Free existing pbuf reference + pbuf_free(_pb); + } + + // Copy all members + _udp = packet._udp; + _pb = packet._pb; + _if = packet._if; + _data = packet._data; + _len = packet._len; + _index = 0; + + memcpy(&_remoteIp, &packet._remoteIp, sizeof(ip_addr_t)); + memcpy(&_localIp, &packet._localIp, sizeof(ip_addr_t)); + _localPort = packet._localPort; + _remotePort = packet._remotePort; + memcpy(_remoteMac, packet._remoteMac, 6); + + // Increment reference count for the new pbuf + pbuf_ref(_pb); + } + return *this; +} + AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr, uint16_t rport, struct netif *ntif) { _udp = udp; _pb = pb; diff --git a/libraries/AsyncUDP/src/AsyncUDP.h b/libraries/AsyncUDP/src/AsyncUDP.h index cd96d852542..9f4778888f0 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.h +++ b/libraries/AsyncUDP/src/AsyncUDP.h @@ -100,6 +100,9 @@ class AsyncUDPPacket : public Stream { size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); + + // Copy assignment operator + AsyncUDPPacket &operator=(const AsyncUDPPacket &packet); }; class AsyncUDP : public Print { diff --git a/libraries/BLE/README.md b/libraries/BLE/README.md index 759c8526f0f..b89d556dcc9 100644 --- a/libraries/BLE/README.md +++ b/libraries/BLE/README.md @@ -1,12 +1,467 @@ -# ESP32 BLE for Arduino -The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino. +# BLE for ESP32 Arduino Core + +A comprehensive reference for ESP32 Bluetooth Low Energy (BLE) pairing and security implementation using the ESP32 Arduino BLE library. + +## Overview + +This guide provides ESP32 developers with comprehensive information about BLE security implementation using the ESP32 Arduino BLE library. It covers both Bluedroid (ESP32) and NimBLE (other SoCs) implementations with realistic scenarios and troubleshooting guidance. + +Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues
(please don't use https://github.com/nkolban/esp32-snippets/issues or https://github.com/h2zero/NimBLE-Arduino/issues) + +## Security + +### Quick Start + +1. **Choose your ESP32's IO capabilities** using `ESP_IO_CAP_*` constants +2. **Configure authentication requirements** with properties or permissions +3. **Set up security** using `BLESecurity` class methods +4. **Handle stack differences** between Bluedroid (ESP32) and NimBLE (other SoCs) +5. **Test with NVS clearing** during development + +### Understanding BLE Pairing + +#### Pairing vs Bonding +- **Pairing**: The process of establishing encryption keys between devices +- **Bonding**: Storing those keys for future reconnections (persistent pairing) + +#### Pairing Types +- **Legacy Pairing**: Original BLE pairing (Bluetooth 4.0/4.1) +- **Secure Connections**: Enhanced security (Bluetooth 4.2+) using FIPS-approved algorithms + +#### Security Levels +- **Just Works**: Encryption without user verification (vulnerable to passive eavesdropping) +- **MITM Protected**: User verification prevents man-in-the-middle attacks + +### IO Capabilities Explained + +The ESP32 BLE library defines the following IO capabilities: + +| Capability | Library Constant | Can Display | Can Input | Can Confirm | Example Devices | +|------------|-----------------|-------------|-----------|-------------|-----------------| +| **No Input No Output** | `ESP_IO_CAP_NONE` | ❌ | ❌ | ❌ | Sensor nodes, beacons, simple actuators | +| **Display Only** | `ESP_IO_CAP_OUT` | ✅ | ❌ | ❌ | E-ink displays, LED matrix displays | +| **Keyboard Only** | `ESP_IO_CAP_IN` | ❌ | ✅ | ❌ | Button-only devices, rotary encoders | +| **Display Yes/No** | `ESP_IO_CAP_IO` | ✅ | ❌ | ✅ | Devices with display + confirmation button | +| **Keyboard Display** | `ESP_IO_CAP_KBDISP` | ✅ | ✅ | ✅ | Full-featured ESP32 devices with UI | + +### Pairing Methods Explained + +#### 🔓 Just Works +- **Security**: Encryption only (no MITM protection) +- **User Experience**: Automatic, no user interaction +- **Use Case**: Convenience over security (fitness trackers, mice) +- **Vulnerability**: Susceptible to passive eavesdropping during pairing + +#### 🔐 Passkey Entry +- **Security**: Full MITM protection +- **User Experience**: One device shows 6-digit code, other inputs it +- **Use Case**: Keyboards pairing to computers +- **Process**: + 1. Display device shows random 6-digit number (000000-999999) + 2. Input device user types the number + 3. Pairing succeeds if numbers match + +#### 🔐 Numeric Comparison (Secure Connections Only) +- **Security**: Full MITM protection +- **User Experience**: Both devices show same number, user confirms match +- **Use Case**: Two smartphones/tablets pairing +- **Process**: + 1. Both devices display identical 6-digit number + 2. User verifies numbers match on both screens + 3. User confirms "Yes" on both devices + +#### 🔐 Out-of-Band (OOB) (Not supported by this library) +- **Security**: Highest security level +- **User Experience**: Uses external channel (NFC, QR code) +- **Use Case**: High-security applications +- **Priority**: Always used when OOB data is available + +### Pairing Methods Compatibility Matrix + +Here is the compatibility matrix for the pairing methods depending on the IO capabilities of the devices. +Note that the initiator is the device that starts the pairing process (usually the client) and the responder is +the device that accepts the pairing request (usually the server). + +![Pairing Methods Compatibility Matrix](https://www.bluetooth.com/wp-content/uploads/2016/06/screen-shot-06-08-16-at-0124-pm.png) + +### Bluedroid vs NimBLE + +Bluedroid and NimBLE are two different Bluetooth stack implementations. + +#### Bluedroid + +Bluedroid is the default Bluetooth stack in ESP-IDF. It supports both Bluetooth Classic and Bluetooth LE. It is used by the ESP32 in the Arduino Core. + +Bluedroid requires more flash and RAM than NimBLE and access permissions for characteristics and descriptors are set using a specific API through the `setAccessPermissions()` function. The original source of the Bluedroid project, **which is not maintained anymore**, can be found here: https://github.com/nkolban/esp32-snippets -Some parts of the NimBLE implementation are based on the work of h2zero, which can be found here: https://github.com/h2zero/NimBLE-Arduino +**Bluedroid will be replaced by NimBLE in version 4.0.0 of the Arduino Core. Bluetooth Classic and Bluedroid will no longer be supported but can be used by using Arduino as an ESP-IDF component.** + +#### NimBLE + +NimBLE is a lightweight Bluetooth stack for Bluetooth LE only. It is used by all SoCs that are not the ESP32. + +NimBLE requires less flash and RAM than Bluedroid. Access permissions for characteristics are set using exclusive properties in the characteristic creation. Access permissions for descriptors are set using the `setAccessPermissions()` function just like in Bluedroid. + +Some parts of the NimBLE implementation are based on the work of h2zero, which can be found here: https://github.com/h2zero/NimBLE-Arduino. For a more customizable and feature-rich implementation of the NimBLE stack, you can use the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. + +### Common Scenarios + +Here are some common scenarios for the pairing methods depending on the IO capabilities of the devices. Check also the secure BLE examples in the ESP32 Arduino Core for more detailed usage examples. + +#### Scenario 1: Mobile App ↔ ESP32 Sensor Node +- **Devices**: Mobile (`ESP_IO_CAP_IO`) ↔ ESP32 Sensor (`ESP_IO_CAP_NONE`) +- **MITM**: Not achievable with this IO combination (falls back to Just Works) +- **Characteristic Authentication**: Bonding only +- **Result**: Just Works with bonding for reconnection +- **Use Case**: Weather stations, environmental monitors + +#### Scenario 2: ESP32 Smart Lock ↔ Mobile App +- **Devices**: ESP32 Lock (`ESP_IO_CAP_OUT`) ↔ Mobile (`ESP_IO_CAP_KBDISP`) +- **MITM**: Required for security +- **Characteristic Authentication**: Bonding + Secure Connection + MITM +- **Result**: Passkey Entry (ESP32 displays, mobile enters) +- **Implementation**: Static passkey or dynamic display + +#### Scenario 3: ESP32 Configuration Device ↔ Admin Tool +- **Devices**: ESP32 (`ESP_IO_CAP_KBDISP`) ↔ Admin Tool (`ESP_IO_CAP_KBDISP`) +- **MITM**: Required for configuration security +- **Characteristic Authentication**: Bonding + Secure Connection + MITM +- **Result**: + - Legacy: Passkey Entry + - Secure Connections: Numeric Comparison +- **Use Case**: Industrial IoT configuration, network setup + +#### Scenario 4: ESP32 Beacon ↔ Scanner App +- **Devices**: ESP32 Beacon (`ESP_IO_CAP_NONE`) ↔ Scanner (`ESP_IO_CAP_IO`) +- **MITM**: Not required (broadcast only) +- **Characteristic Authentication**: None +- **Result**: No pairing required +- **Use Case**: Asset tracking, proximity detection + +#### Scenario 5: ESP32 Smart Home Hub ↔ Multiple Devices +- **Devices**: ESP32 Hub (`ESP_IO_CAP_IO`) ↔ Various sensors (`ESP_IO_CAP_NONE`) +- **MITM**: Not possible when any of the peers are `ESP_IO_CAP_NONE` +- **Characteristic Authentication**: Bonding only (no MITM possible with `ESP_IO_CAP_NONE`) +- **Result**: Just Works only +- **Use Case**: Centralized home automation controller + +### Implementation Guidelines + +#### For ESP32 Device Developers + +##### Choosing IO Capabilities +```cpp +#include + +// Conservative approach - limits pairing methods but ensures compatibility +pSecurity->setCapability(ESP_IO_CAP_NONE); // Just Works only + +// Balanced approach - good UX with optional security +pSecurity->setCapability(ESP_IO_CAP_IO); // Just Works or Numeric Comparison + +// Maximum security - supports all methods +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // All pairing methods available +``` + +##### Authentication Configuration +```cpp +BLESecurity *pSecurity = new BLESecurity(); + +// Low security applications (sensors, environmental monitoring) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_NO_BOND); + +// Standard security with bonding (smart home devices) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); + +// MITM protection required (access control, payments) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_BOND); + +// Maximum security with Secure Connections (critical systems) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); + +// Alternative syntax (more readable for complex requirements) +pSecurity->setAuthenticationMode(true, true, true); // Bonding, MITM, Secure Connections +``` + +##### Static Passkey Example (from secure examples) +```cpp +// Set a static passkey for consistent pairing experience +#define DEVICE_PASSKEY 123456 +pSecurity->setPassKey(true, DEVICE_PASSKEY); // static=true, passkey=123456 +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // Required for MITM even with static passkey +``` + +### Security Considerations + +#### When to Require MITM +- **Always**: Payment systems, medical devices, access control +- **Usually**: File transfers, personal data sync, keyboards +- **Optional**: Fitness trackers, environmental sensors, mice +- **Never**: Beacons, broadcast-only devices + +#### Legacy vs Secure Connections +- **Legacy**: Compatible with all BLE devices (2010+) +- **Secure Connections**: Better security but requires Bluetooth 4.2+ (2014+) +- **Recommendation**: Support both, prefer Secure Connections when available + +#### Implementation Differences +```cpp +// Basic characteristic properties (both stacks) +uint32_t properties = BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE; + +// NimBLE: Add authentication properties (ignored by Bluedroid) +properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_WRITE_AUTHEN; + +BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHAR_UUID, properties); + +// Bluedroid: Set access permissions (ignored by NimBLE) +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); + +// Check which stack is running +String stackType = BLEDevice::getBLEStackString(); +Serial.println("Using BLE stack: " + stackType); +``` + +#### Known Vulnerabilities +1. **Just Works**: Vulnerable to passive eavesdropping during initial pairing +2. **Legacy Pairing**: Uses weaker cryptographic algorithms +3. **Passkey Brute Force**: 6-digit passkeys have only 1M combinations +4. **Physical Security**: Displayed passkeys can be shoulder-surfed + +### Troubleshooting + +#### Common Issues + +##### Pairing Always Uses Just Works +```cpp +// ❌ Problem: Missing MITM flag +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + +// ✅ Solution: Add MITM protection +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); +``` + +##### Static passkey not being requested / Nothing happens when trying to read secure characteristic +```cpp +// ❌ Problem: Wrong IO capability for MITM +pSecurity->setCapability(ESP_IO_CAP_NONE); // Can't support MITM + +// ✅ Solution: Set proper capability even for static passkey +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // Required for MITM +pSecurity->setPassKey(true, 123456); +``` + +##### Secure Characteristic Access Fails +```cpp +// ❌ Problem: Wrong security method for stack +// Bluedroid approach (won't work on NimBLE) +uint32_t properties = BLECharacteristic::PROPERTY_READ; +pCharacteristic = pService->createCharacteristic(uuid, properties); +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM); // Ignored by NimBLE! + +// ✅ Solution: Use both methods for cross-compatibility +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_READ_AUTHEN; // For NimBLE +pCharacteristic = pService->createCharacteristic(uuid, properties); +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM); // For Bluedroid +``` + +##### Pairing Works Once, Then Fails (NVS Cache Issue) +```cpp +// ✅ Solution: Clear NVS for testing/development +Serial.println("Clearing NVS pairing data for testing..."); +nvs_flash_erase(); +nvs_flash_init(); +``` + +##### Default Passkey Warning +``` +*WARNING* Using default passkey: 123456 +*WARNING* Please use a random passkey or set a different static passkey +``` +```cpp +// ✅ Solution: Change from default +#define CUSTOM_PASSKEY 567890 // Your unique passkey +pSecurity->setPassKey(true, CUSTOM_PASSKEY); +``` + +##### Connection drops during pairing +```cpp +// ✅ Solution: Implement security callbacks for better error handling +class MySecurityCallbacks : public BLESecurityCallbacks { + void onAuthenticationComplete(esp_ble_auth_cmpl_t param) override { + if (param.success) { + Serial.println("Pairing successful!"); + } else { + Serial.printf("Pairing failed, reason: %d\n", param.fail_reason); + } + } +}; + +BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); +``` + +#### Cross-Platform Best Practice +```cpp +// Always use both methods for maximum compatibility +uint32_t secure_properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHEN | // NimBLE + BLECharacteristic::PROPERTY_WRITE_AUTHEN; // NimBLE + +BLECharacteristic *pChar = pService->createCharacteristic(uuid, secure_properties); + +// Bluedroid permissions (ignored by NimBLE, but doesn't hurt) +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); +``` + +### Complete Properties and Permissions Reference + +#### Bluedroid + +Bluedroid uses properties to define the capabilities of a characteristic and permissions to define the access permissions. NimBLE will ignore the access permissions. + +##### Supported Properties +```cpp +BLECharacteristic::PROPERTY_READ // Read operation +BLECharacteristic::PROPERTY_WRITE // Write operation +BLECharacteristic::PROPERTY_WRITE_NR // Write without response +BLECharacteristic::PROPERTY_NOTIFY // Notifications +BLECharacteristic::PROPERTY_INDICATE // Indications +BLECharacteristic::PROPERTY_BROADCAST // Broadcast +``` + +##### Characteristic and Descriptor Access Permissions +```cpp +// Basic permissions +ESP_GATT_PERM_READ // Read allowed +ESP_GATT_PERM_WRITE // Write allowed + +// Encryption required +ESP_GATT_PERM_READ_ENCRYPTED // Read requires encryption +ESP_GATT_PERM_WRITE_ENCRYPTED // Write requires encryption + +// Authentication required (MITM protection) +ESP_GATT_PERM_READ_ENC_MITM // Read requires encryption + MITM +ESP_GATT_PERM_WRITE_ENC_MITM // Write requires encryption + MITM + +// Authorization required +ESP_GATT_PERM_READ_AUTHORIZATION // Read requires authorization callback +ESP_GATT_PERM_WRITE_AUTHORIZATION // Write requires authorization callback +``` + +#### NimBLE + +NimBLE uses properties to define both the capabilities of a characteristic and the access permissions. Bluedroid will ignore the NimBLE exclusive properties. + +##### Supported Properties +```cpp +// Basic properties +BLECharacteristic::PROPERTY_READ // Read operation +BLECharacteristic::PROPERTY_WRITE // Write operation +BLECharacteristic::PROPERTY_WRITE_NR // Write without response +BLECharacteristic::PROPERTY_NOTIFY // Notifications +BLECharacteristic::PROPERTY_INDICATE // Indications +BLECharacteristic::PROPERTY_BROADCAST // Broadcast + +// NimBLE specific properties + +// Encryption required +BLECharacteristic::PROPERTY_READ_ENC // Read requires encryption +BLECharacteristic::PROPERTY_WRITE_ENC // Write requires encryption + +// Authentication required (MITM protection) +BLECharacteristic::PROPERTY_READ_AUTHEN // Read requires encryption + MITM protection +BLECharacteristic::PROPERTY_WRITE_AUTHEN // Write requires encryption + MITM protection + +// Authorization required +BLECharacteristic::PROPERTY_READ_AUTHOR // Read requires authorization callback +BLECharacteristic::PROPERTY_WRITE_AUTHOR // Write requires authorization callback +``` + +##### Descriptor Access Permissions +```cpp +// Basic permissions +ESP_GATT_PERM_READ // Read allowed +ESP_GATT_PERM_WRITE // Write allowed + +// Encryption required +ESP_GATT_PERM_READ_ENCRYPTED // Read requires encryption +ESP_GATT_PERM_WRITE_ENCRYPTED // Write requires encryption + +// Authentication required (MITM protection) +ESP_GATT_PERM_READ_ENC_MITM // Read requires encryption + MITM +ESP_GATT_PERM_WRITE_ENC_MITM // Write requires encryption + MITM + +// Authorization required +ESP_GATT_PERM_READ_AUTHORIZATION // Read requires authorization callback +ESP_GATT_PERM_WRITE_AUTHORIZATION // Write requires authorization callback +``` + +#### Usage Examples by Security Level + +##### No Security (Both Stacks) +```cpp +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +``` + +##### Encryption Only +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_ENC | + BLECharacteristic::PROPERTY_WRITE_ENC; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); +``` + +##### MITM Protection (Authentication) +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHEN | + BLECharacteristic::PROPERTY_WRITE_AUTHEN; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); +``` + +##### Authorization Required +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHOR | + BLECharacteristic::PROPERTY_WRITE_AUTHOR; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_AUTHORIZATION | ESP_GATT_PERM_WRITE_AUTHORIZATION); +``` + +#### Debug Tips +1. **Log pairing features** exchanged between devices +2. **Monitor pairing method** selected by the stack +3. **Check timeout values** for user input methods +4. **Verify key distribution** flags match on both sides + +### Standards References -Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues
(please don't use https://github.com/nkolban/esp32-snippets/issues or https://github.com/h2zero/NimBLE-Arduino/issues!) +- **Bluetooth Core Specification v5.4**: Volume 3, Part H (Security Manager) +- **Bluetooth Assigned Numbers**: IO Capability values +- **FIPS-140-2**: Cryptographic standards for Secure Connections -Documentation for using the library can be found here: https://github.com/nkolban/esp32-snippets/tree/master/Documentation +--- -For a more customizable and feature-rich implementation of the NimBLE stack, you can use the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. +*This guide is based on the ESP32 Arduino BLE library implementation and the official Bluetooth Core Specification. For the latest API documentation, refer to the ESP32 Arduino BLE library source code and examples.* diff --git a/libraries/BLE/examples/BLE5_extended_scan/ci.json b/libraries/BLE/examples/BLE5_extended_scan/ci.json deleted file mode 100644 index 8e938055cf2..00000000000 --- a/libraries/BLE/examples/BLE5_extended_scan/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_50_SUPPORTED=y", - "CONFIG_BLUEDROID_ENABLED=y" - ] -} diff --git a/libraries/BLE/examples/BLE5_extended_scan/ci.yml b/libraries/BLE/examples/BLE5_extended_scan/ci.yml new file mode 100644 index 00000000000..304e725169c --- /dev/null +++ b/libraries/BLE/examples/BLE5_extended_scan/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_BLE_50_SUPPORTED=y + - CONFIG_BLUEDROID_ENABLED=y diff --git a/libraries/BLE/examples/BLE5_multi_advertising/ci.json b/libraries/BLE/examples/BLE5_multi_advertising/ci.json deleted file mode 100644 index 8e938055cf2..00000000000 --- a/libraries/BLE/examples/BLE5_multi_advertising/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_50_SUPPORTED=y", - "CONFIG_BLUEDROID_ENABLED=y" - ] -} diff --git a/libraries/BLE/examples/BLE5_multi_advertising/ci.yml b/libraries/BLE/examples/BLE5_multi_advertising/ci.yml new file mode 100644 index 00000000000..304e725169c --- /dev/null +++ b/libraries/BLE/examples/BLE5_multi_advertising/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_BLE_50_SUPPORTED=y + - CONFIG_BLUEDROID_ENABLED=y diff --git a/libraries/BLE/examples/BLE5_periodic_advertising/ci.json b/libraries/BLE/examples/BLE5_periodic_advertising/ci.json deleted file mode 100644 index 8e938055cf2..00000000000 --- a/libraries/BLE/examples/BLE5_periodic_advertising/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_50_SUPPORTED=y", - "CONFIG_BLUEDROID_ENABLED=y" - ] -} diff --git a/libraries/BLE/examples/BLE5_periodic_advertising/ci.yml b/libraries/BLE/examples/BLE5_periodic_advertising/ci.yml new file mode 100644 index 00000000000..304e725169c --- /dev/null +++ b/libraries/BLE/examples/BLE5_periodic_advertising/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_BLE_50_SUPPORTED=y + - CONFIG_BLUEDROID_ENABLED=y diff --git a/libraries/BLE/examples/BLE5_periodic_sync/ci.json b/libraries/BLE/examples/BLE5_periodic_sync/ci.json deleted file mode 100644 index 8e938055cf2..00000000000 --- a/libraries/BLE/examples/BLE5_periodic_sync/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_50_SUPPORTED=y", - "CONFIG_BLUEDROID_ENABLED=y" - ] -} diff --git a/libraries/BLE/examples/BLE5_periodic_sync/ci.yml b/libraries/BLE/examples/BLE5_periodic_sync/ci.yml new file mode 100644 index 00000000000..304e725169c --- /dev/null +++ b/libraries/BLE/examples/BLE5_periodic_sync/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_BLE_50_SUPPORTED=y + - CONFIG_BLUEDROID_ENABLED=y diff --git a/libraries/BLE/examples/Beacon_Scanner/ci.json b/libraries/BLE/examples/Beacon_Scanner/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Beacon_Scanner/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Beacon_Scanner/ci.yml b/libraries/BLE/examples/Beacon_Scanner/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Beacon_Scanner/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Client/ci.json b/libraries/BLE/examples/Client/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Client/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Client/ci.yml b/libraries/BLE/examples/Client/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Client/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Client_multiconnect/Client_multiconnect.ino b/libraries/BLE/examples/Client_multiconnect/Client_multiconnect.ino new file mode 100644 index 00000000000..712e5bbffe2 --- /dev/null +++ b/libraries/BLE/examples/Client_multiconnect/Client_multiconnect.ino @@ -0,0 +1,276 @@ +/** + * A BLE client example that connects to multiple BLE servers simultaneously. + * + * This example demonstrates how to: + * - Scan for multiple BLE servers + * - Connect to multiple servers at the same time + * - Interact with characteristics on different servers + * - Handle disconnections and reconnections + * + * The example looks for servers advertising the service UUID: 4fafc201-1fb5-459e-8fcc-c5c9c331914b + * and connects to up to MAX_SERVERS servers. + * + * Created by lucasssvaz + * Based on the original Client example by Neil Kolban and chegewara + */ + +#include "BLEDevice.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristic of the remote service we are interested in. +static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); + +// Maximum number of servers to connect to +#define MAX_SERVERS 3 + +// Structure to hold information about each connected server +struct ServerConnection { + BLEClient *pClient; + BLEAdvertisedDevice *pDevice; + BLERemoteCharacteristic *pRemoteCharacteristic; + bool connected; + bool doConnect; + String name; +}; + +// Array to manage multiple server connections +ServerConnection servers[MAX_SERVERS]; +int connectedServers = 0; +static bool doScan = true; + +// Callback function to handle notifications from any server +static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { + // Find which server this notification came from + for (int i = 0; i < MAX_SERVERS; i++) { + if (servers[i].connected && servers[i].pRemoteCharacteristic == pBLERemoteCharacteristic) { + Serial.print("Notify from server "); + Serial.print(servers[i].name); + Serial.print(" - Characteristic: "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" | Length: "); + Serial.print(length); + Serial.print(" | Data: "); + Serial.write(pData, length); + Serial.println(); + break; + } + } +} + +// Client callback class to handle connect/disconnect events +class MyClientCallback : public BLEClientCallbacks { + int serverIndex; + +public: + MyClientCallback(int index) : serverIndex(index) {} + + void onConnect(BLEClient *pclient) { + Serial.print("Connected to server "); + Serial.println(servers[serverIndex].name); + } + + void onDisconnect(BLEClient *pclient) { + servers[serverIndex].connected = false; + connectedServers--; + Serial.print("Disconnected from server "); + Serial.print(servers[serverIndex].name); + Serial.print(" | Total connected: "); + Serial.println(connectedServers); + doScan = true; // Resume scanning to find replacement servers + } +}; + +// Function to connect to a specific server +bool connectToServer(int serverIndex) { + Serial.print("Connecting to server "); + Serial.print(serverIndex); + Serial.print(" at address: "); + Serial.println(servers[serverIndex].pDevice->getAddress().toString().c_str()); + + servers[serverIndex].pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + // Set the callback for this specific server connection + servers[serverIndex].pClient->setClientCallbacks(new MyClientCallback(serverIndex)); + + // Connect to the remote BLE Server + servers[serverIndex].pClient->connect(servers[serverIndex].pDevice); + Serial.println(" - Connected to server"); + servers[serverIndex].pClient->setMTU(517); // Request maximum MTU from server + + // Obtain a reference to the service we are after in the remote BLE server + BLERemoteService *pRemoteService = servers[serverIndex].pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + servers[serverIndex].pClient->disconnect(); + return false; + } + Serial.println(" - Found service"); + + // Obtain a reference to the characteristic in the service + servers[serverIndex].pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID); + if (servers[serverIndex].pRemoteCharacteristic == nullptr) { + Serial.print("Failed to find characteristic UUID: "); + Serial.println(charUUID.toString().c_str()); + servers[serverIndex].pClient->disconnect(); + return false; + } + Serial.println(" - Found characteristic"); + + // Read the value of the characteristic + if (servers[serverIndex].pRemoteCharacteristic->canRead()) { + String value = servers[serverIndex].pRemoteCharacteristic->readValue(); + Serial.print("Initial characteristic value: "); + Serial.println(value.c_str()); + } + + // Register for notifications if available + if (servers[serverIndex].pRemoteCharacteristic->canNotify()) { + servers[serverIndex].pRemoteCharacteristic->registerForNotify(notifyCallback); + Serial.println(" - Registered for notifications"); + } + + servers[serverIndex].connected = true; + connectedServers++; + Serial.print("Successfully connected! Total servers connected: "); + Serial.println(connectedServers); + return true; +} + +// Scan callback class to find BLE servers +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // Check if this device has the service we're looking for + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + Serial.println(" -> This device has our service!"); + + // Check if we already know about this device + String deviceAddress = advertisedDevice.getAddress().toString().c_str(); + bool alreadyKnown = false; + + for (int i = 0; i < MAX_SERVERS; i++) { + if (servers[i].pDevice != nullptr) { + if (servers[i].pDevice->getAddress().toString() == deviceAddress) { + alreadyKnown = true; + break; + } + } + } + + if (alreadyKnown) { + Serial.println(" -> Already connected or connecting to this device"); + return; + } + + // Find an empty slot for this server + for (int i = 0; i < MAX_SERVERS; i++) { + if (servers[i].pDevice == nullptr || (!servers[i].connected && !servers[i].doConnect)) { + servers[i].pDevice = new BLEAdvertisedDevice(advertisedDevice); + servers[i].doConnect = true; + servers[i].name = "Server_" + String(i); + Serial.print(" -> Assigned to slot "); + Serial.println(i); + + // If we've found enough servers, stop scanning + int pendingConnections = 0; + for (int j = 0; j < MAX_SERVERS; j++) { + if (servers[j].connected || servers[j].doConnect) { + pendingConnections++; + } + } + if (pendingConnections >= MAX_SERVERS) { + Serial.println("Found enough servers, stopping scan"); + BLEDevice::getScan()->stop(); + doScan = false; + } + break; + } + } + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("================================="); + Serial.println("BLE Multi-Client Example"); + Serial.println("================================="); + Serial.print("Max servers to connect: "); + Serial.println(MAX_SERVERS); + Serial.println(); + + // Initialize all server connections + for (int i = 0; i < MAX_SERVERS; i++) { + servers[i].pClient = nullptr; + servers[i].pDevice = nullptr; + servers[i].pRemoteCharacteristic = nullptr; + servers[i].connected = false; + servers[i].doConnect = false; + servers[i].name = ""; + } + + // Initialize BLE + BLEDevice::init("ESP32_MultiClient"); + + // Set up BLE scanner + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); + + Serial.println("Scanning for BLE servers..."); +} + +void loop() { + // Process any pending connections + for (int i = 0; i < MAX_SERVERS; i++) { + if (servers[i].doConnect) { + if (connectToServer(i)) { + Serial.println("Connection successful"); + } else { + Serial.println("Connection failed"); + // Clear this slot so we can try another server + delete servers[i].pDevice; + servers[i].pDevice = nullptr; + } + servers[i].doConnect = false; + } + } + + // If we're connected to servers, send data to each one + if (connectedServers > 0) { + for (int i = 0; i < MAX_SERVERS; i++) { + if (servers[i].connected && servers[i].pRemoteCharacteristic != nullptr) { + // Create a unique message for each server + String newValue = servers[i].name + " | Time: " + String(millis() / 1000); + + Serial.print("Sending to "); + Serial.print(servers[i].name); + Serial.print(": "); + Serial.println(newValue); + + // Write the value to the characteristic + servers[i].pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length()); + } + } + } else { + Serial.println("No servers connected"); + } + + // Resume scanning if we have room for more connections + if (doScan && connectedServers < MAX_SERVERS) { + Serial.println("Resuming scan for more servers..."); + BLEDevice::getScan()->start(5, false); + doScan = false; + delay(5000); // Wait for scan to complete + } + + delay(2000); // Delay between loop iterations +} diff --git a/libraries/BLE/examples/Client_multiconnect/ci.yml b/libraries/BLE/examples/Client_multiconnect/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Client_multiconnect/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino b/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino new file mode 100644 index 00000000000..a3204fcb044 --- /dev/null +++ b/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino @@ -0,0 +1,327 @@ +/* + Secure client with static passkey and IRK retrieval + + This example demonstrates how to create a secure BLE client that connects to + a secure BLE server using a static passkey without prompting the user. + The client will automatically use the same passkey (123456) as the server. + + After successful bonding, the example demonstrates how to retrieve the + server's Identity Resolving Key (IRK) in multiple formats: + - Comma-separated hex format: 0x1A,0x1B,0x1C,... + - Base64 encoded (for Home Assistant Private BLE Device service) + - Reverse hex order (for Home Assistant ESPresense) + + This client is designed to work with the Server_secure_static_passkey example. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + This means that in NimBLE you can read the insecure characteristic without entering + the passkey. This is not possible in Bluedroid. + + IMPORTANT: + - MITM (Man-In-The-Middle protection) must be enabled for password prompts to work. + - Bonding must be enabled to store and retrieve the IRK. + - The server must distribute its Identity Key during pairing. + + Based on examples from Neil Kolban and h2zero. + Created by lucasssvaz. +*/ + +#include "BLEDevice.h" +#include "BLESecurity.h" +#include "nvs_flash.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristics of the remote service we are interested in. +static BLEUUID insecureCharUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); +static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8"); + +// This must match the server's passkey +#define CLIENT_PIN 123456 + +static boolean doConnect = false; +static boolean connected = false; +static boolean doScan = false; +static BLEClient *pClient = nullptr; +static BLERemoteCharacteristic *pRemoteInsecureCharacteristic; +static BLERemoteCharacteristic *pRemoteSecureCharacteristic; +static BLEAdvertisedDevice *myDevice; + +// Print an IRK buffer as hex with leading zeros and ':' separator +static void printIrkBinary(uint8_t *irk) { + for (int i = 0; i < 16; i++) { + if (irk[i] < 0x10) { + Serial.print("0"); + } + Serial.print(irk[i], HEX); + if (i < 15) { + Serial.print(":"); + } + } +} + +static void get_peer_irk(BLEAddress peerAddr) { + Serial.println("\n=== Retrieving peer IRK (Server) ===\n"); + + uint8_t irk[16]; + + // Get IRK in binary format + if (BLEDevice::getPeerIRK(peerAddr, irk)) { + Serial.println("Successfully retrieved peer IRK in binary format:"); + printIrkBinary(irk); + Serial.println("\n"); + } + + // Get IRK in different string formats + String irkString = BLEDevice::getPeerIRKString(peerAddr); + String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr); + String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr); + + if (irkString.length() > 0) { + Serial.println("Successfully retrieved peer IRK in multiple formats:\n"); + Serial.print("IRK (comma-separated hex): "); + Serial.println(irkString); + Serial.print("IRK (Base64 for Home Assistant Private BLE Device): "); + Serial.println(irkBase64); + Serial.print("IRK (reverse hex for Home Assistant ESPresense): "); + Serial.println(irkReverse); + Serial.println(); + } else { + Serial.println("!!! Failed to retrieve peer IRK !!!"); + Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key."); + Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n"); + } + + Serial.println("=======================================\n"); +} + +// Callback function to handle notifications +static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("data: "); + Serial.write(pData, length); + Serial.println(); +} + +class MyClientCallback : public BLEClientCallbacks { + void onConnect(BLEClient *pclient) { + Serial.println("Connected to secure server"); + } + + void onDisconnect(BLEClient *pclient) { + connected = false; + Serial.println("Disconnected from server"); + } +}; + +// Security callbacks to print IRKs once authentication completes +class MySecurityCallbacks : public BLESecurityCallbacks { +#if defined(CONFIG_BLUEDROID_ENABLED) + void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override { + // Print the IRK received by the peer + BLEAddress peerAddr(desc.bd_addr); + get_peer_irk(peerAddr); + } +#endif + +#if defined(CONFIG_NIMBLE_ENABLED) + void onAuthenticationComplete(ble_gap_conn_desc *desc) override { + // Print the IRK received by the peer + BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type); + get_peer_irk(peerAddr); + } +#endif +}; + +bool connectToServer() { + Serial.print("Forming a secure connection to "); + Serial.println(myDevice->getAddress().toString().c_str()); + + pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + pClient->setClientCallbacks(new MyClientCallback()); + + // Connect to the remote BLE Server. + pClient->connect(myDevice); + Serial.println(" - Connected to server"); + + // Set MTU to maximum for better performance + pClient->setMTU(517); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService *pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our service"); + + // Obtain a reference to the insecure characteristic + pRemoteInsecureCharacteristic = pRemoteService->getCharacteristic(insecureCharUUID); + if (pRemoteInsecureCharacteristic == nullptr) { + Serial.print("Failed to find insecure characteristic UUID: "); + Serial.println(insecureCharUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found insecure characteristic"); + + // Obtain a reference to the secure characteristic + pRemoteSecureCharacteristic = pRemoteService->getCharacteristic(secureCharUUID); + if (pRemoteSecureCharacteristic == nullptr) { + Serial.print("Failed to find secure characteristic UUID: "); + Serial.println(secureCharUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found secure characteristic"); + + // Read the value of the insecure characteristic (should work without authentication) + if (pRemoteInsecureCharacteristic->canRead()) { + String value = pRemoteInsecureCharacteristic->readValue(); + Serial.print("Insecure characteristic value: "); + Serial.println(value.c_str()); + } + + // For Bluedroid, we need to set the authentication request type for the secure characteristic + // This is not needed for NimBLE and will be ignored. + pRemoteSecureCharacteristic->setAuth(ESP_GATT_AUTH_REQ_MITM); + + // Try to read the secure characteristic (this will trigger security negotiation in NimBLE) + if (pRemoteSecureCharacteristic->canRead()) { + Serial.println("Attempting to read secure characteristic..."); + String value = pRemoteSecureCharacteristic->readValue(); + Serial.print("Secure characteristic value: "); + Serial.println(value.c_str()); + } + + // Register for notifications on both characteristics if they support it + if (pRemoteInsecureCharacteristic->canNotify()) { + pRemoteInsecureCharacteristic->registerForNotify(notifyCallback); + Serial.println(" - Registered for insecure characteristic notifications"); + } + + if (pRemoteSecureCharacteristic->canNotify()) { + pRemoteSecureCharacteristic->registerForNotify(notifyCallback); + Serial.println(" - Registered for secure characteristic notifications"); + } + + connected = true; + return true; +} + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + Serial.println("Found our secure server!"); + BLEDevice::getScan()->stop(); + myDevice = new BLEAdvertisedDevice(advertisedDevice); + doConnect = true; + doScan = true; + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting Secure BLE Client application..."); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + BLEDevice::init("Secure BLE Client"); + + // Set up security with the same passkey as the server + BLESecurity *pSecurity = new BLESecurity(); + + // Set security parameters + // Default parameters: + // - IO capability is set to NONE + // - Initiator and responder key distribution flags are set to both encryption and identity keys. + // - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it. + // - Key size is set to 16 bytes + + // Set the same static passkey as the server + // The first argument defines if the passkey is static or random. + // The second argument is the passkey (ignored when using a random passkey). + pSecurity->setPassKey(true, CLIENT_PIN); + + // Set authentication mode to match server requirements + // Enable bonding, MITM (for password prompts), and secure connection for this example + // Bonding is required to store and retrieve the IRK + pSecurity->setAuthenticationMode(true, true, true); + + // Set IO capability to KeyboardOnly + // We need the proper IO capability for MITM authentication even + // if the passkey is static and won't be entered by the user + // See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/ + pSecurity->setCapability(ESP_IO_CAP_IN); + + // Set callbacks to handle authentication completion and print IRKs + BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); +} + +void loop() { + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. + if (doConnect == true) { + if (connectToServer()) { + Serial.println("We are now connected to the secure BLE Server."); + } else { + Serial.println("We have failed to connect to the server; there is nothing more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, demonstrate secure communication + if (connected) { + // Write to the insecure characteristic + String insecureValue = "Client time: " + String(millis() / 1000); + if (pRemoteInsecureCharacteristic->canWrite()) { + pRemoteInsecureCharacteristic->writeValue(insecureValue.c_str(), insecureValue.length()); + Serial.println("Wrote to insecure characteristic: " + insecureValue); + } + + // Write to the secure characteristic + String secureValue = "Secure client time: " + String(millis() / 1000); + if (pRemoteSecureCharacteristic->canWrite()) { + pRemoteSecureCharacteristic->writeValue(secureValue.c_str(), secureValue.length()); + Serial.println("Wrote to secure characteristic: " + secureValue); + } + } else if (doScan) { + // Restart scanning if we're disconnected + BLEDevice::getScan()->start(0); + } + + delay(2000); // Delay 2 seconds between loops +} diff --git a/libraries/BLE/examples/Client_secure_static_passkey/ci.yml b/libraries/BLE/examples/Client_secure_static_passkey/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Client_secure_static_passkey/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json b/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/EddystoneTLM_Beacon/ci.yml b/libraries/BLE/examples/EddystoneTLM_Beacon/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/EddystoneTLM_Beacon/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/EddystoneURL_Beacon/ci.json b/libraries/BLE/examples/EddystoneURL_Beacon/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/EddystoneURL_Beacon/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/EddystoneURL_Beacon/ci.yml b/libraries/BLE/examples/EddystoneURL_Beacon/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/EddystoneURL_Beacon/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Notify/ci.json b/libraries/BLE/examples/Notify/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Notify/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Notify/ci.yml b/libraries/BLE/examples/Notify/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Notify/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Scan/ci.json b/libraries/BLE/examples/Scan/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Scan/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Scan/ci.yml b/libraries/BLE/examples/Scan/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Scan/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Server/ci.json b/libraries/BLE/examples/Server/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Server/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Server/ci.yml b/libraries/BLE/examples/Server/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Server/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Server_multiconnect/ci.json b/libraries/BLE/examples/Server_multiconnect/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Server_multiconnect/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Server_multiconnect/ci.yml b/libraries/BLE/examples/Server_multiconnect/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Server_multiconnect/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino b/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino new file mode 100644 index 00000000000..b1ab9cd5931 --- /dev/null +++ b/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino @@ -0,0 +1,160 @@ +/* + Simple BLE Server Authorization Example + + This example demonstrates how to create a BLE server with authorization + requirements. It shows the essential setup for: + - Authorization with static passkey + - Secure connection + - MITM (Man-In-The-Middle) protection + + The server creates a single characteristic that requires authorization + to access. Clients must provide the correct passkey (123456) to read + or write to the characteristic. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + + Due to a bug in ESP-IDF's Bluedroid, this example will currently not work with ESP32. + + IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts + to work. Without MITM, the BLE stack assumes no user interaction is needed and will use + "Just Works" pairing method (with encryption if secure connection is enabled). + + Created by lucasssvaz. +*/ + +#include +#include +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +// Example passkey - change this for production use +#define AUTH_PASSKEY 123456 + +static int s_readCount = 0; +static BLECharacteristic *s_pCharacteristic; + +class MySecurityCallbacks : public BLESecurityCallbacks { + bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) { + Serial.println("Authorization request received"); + if (isRead) { + s_readCount++; + // Keep value length <= (MTU - 1) to avoid a follow-up read request + uint16_t maxLen = BLEDevice::getServer()->getPeerMTU(connHandle) - 1; + String msg = "Authorized #" + String(s_readCount); + if (msg.length() > maxLen) { + msg = msg.substring(0, maxLen); + } + s_pCharacteristic->setValue(msg); + // Grant authorization to the first 3 reads + if (s_readCount <= 3) { + Serial.println("Authorization granted"); + return true; + } else { + Serial.println("Authorization denied, read count exceeded"); + Serial.println("Please reset the read counter to continue"); + return false; + } + } + // Fallback to deny + Serial.println("Authorization denied"); + return false; + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE Authorization Example!"); + + // Initialize the BOOT pin for resetting the read count + pinMode(BOOT_PIN, INPUT_PULLUP); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + Serial.print("Using BLE stack: "); + Serial.println(BLEDevice::getBLEStackString()); + + BLEDevice::init("BLE Auth Server"); + + // Set MTU to 517 to avoid a follow-up read request + BLEDevice::setMTU(517); + + // Configure BLE Security + BLESecurity *pSecurity = new BLESecurity(); + + // Set static passkey for authentication + pSecurity->setPassKey(true, AUTH_PASSKEY); + + // Set IO capability to DisplayOnly for MITM authentication + pSecurity->setCapability(ESP_IO_CAP_OUT); + + // Enable authorization requirements: + // - bonding: true (for persistent storage of the keys) + // - MITM: true (enables Man-In-The-Middle protection for password prompts) + // - secure connection: true (enables secure connection for encryption) + pSecurity->setAuthenticationMode(true, true, true); + + // Set the security callbacks + BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); + + // Create BLE Server + BLEServer *pServer = BLEDevice::createServer(); + pServer->advertiseOnDisconnect(true); + + // Create BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create characteristic with read and write properties + uint32_t properties = BLECharacteristic::PROPERTY_READ; + + // For NimBLE: Add authentication properties + // These properties ensure the characteristic requires authorization + // (ignored by Bluedroid but harmless) + properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_AUTHOR; + + s_pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, properties); + + // For Bluedroid: Set access permissions that require encryption and MITM + // This ensures authorization is required (ignored by NimBLE) + s_pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_READ_AUTHORIZATION); + + // Set initial value + s_pCharacteristic->setValue("Hello! You needed authorization to read this!"); + + // Start the service + pService->start(); + + // Configure and start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // helps with iPhone connections + pAdvertising->setMinPreferred(0x12); + + BLEDevice::startAdvertising(); + + Serial.println("BLE Server is running!"); + Serial.println("Authorization is required to access the characteristic."); + Serial.printf("Use passkey: %d when prompted\n", AUTH_PASSKEY); +} + +void loop() { + // Reset the read count if the BOOT pin is pressed + if (digitalRead(BOOT_PIN) == LOW) { + s_readCount = 0; + Serial.println("Read count reset"); + } + + delay(100); +} diff --git a/libraries/BLE/examples/Server_secure_authorization/ci.yml b/libraries/BLE/examples/Server_secure_authorization/ci.yml new file mode 100644 index 00000000000..274bd2b41ed --- /dev/null +++ b/libraries/BLE/examples/Server_secure_authorization/ci.yml @@ -0,0 +1,7 @@ +targets: + esp32: false + +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_BLE_SUPPORTED=y diff --git a/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino b/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino new file mode 100644 index 00000000000..fef8c0bd15f --- /dev/null +++ b/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino @@ -0,0 +1,195 @@ +/* + Secure server with static passkey + + This example demonstrates how to create a secure BLE server with no + IO capability using a static passkey. + The server will accept connections from devices that have the same passkey set. + The example passkey is set to 123456. + The server will create a service and a secure and an insecure characteristic + to be used as example. + + This server is designed to be used with the Client_secure_static_passkey example. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + This means that in NimBLE you can read the insecure characteristic without entering + the passkey. This is not possible in Bluedroid. + + IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts + to work. Without MITM, the BLE stack assumes no user interaction is needed and will use + "Just Works" pairing method (with encryption if secure connection is enabled). + + Based on examples from Neil Kolban and h2zero. + Created by lucasssvaz. +*/ + +#include +#include +#include +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define INSECURE_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +#define SECURE_CHARACTERISTIC_UUID "ff1d2614-e2d6-4c87-9154-6625d39ca7f8" + +// This is an example passkey. You should use a different or random passkey. +#define SERVER_PIN 123456 + +// Print an IRK buffer as hex with leading zeros and ':' separator +static void printIrkBinary(uint8_t *irk) { + for (int i = 0; i < 16; i++) { + if (irk[i] < 0x10) { + Serial.print("0"); + } + Serial.print(irk[i], HEX); + if (i < 15) { + Serial.print(":"); + } + } +} + +static void get_peer_irk(BLEAddress peerAddr) { + Serial.println("\n=== Retrieving peer IRK (Client) ===\n"); + + uint8_t irk[16]; + + // Get IRK in binary format + if (BLEDevice::getPeerIRK(peerAddr, irk)) { + Serial.println("Successfully retrieved peer IRK in binary format:"); + printIrkBinary(irk); + Serial.println("\n"); + } + + // Get IRK in different string formats + String irkString = BLEDevice::getPeerIRKString(peerAddr); + String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr); + String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr); + + if (irkString.length() > 0) { + Serial.println("Successfully retrieved peer IRK in multiple formats:\n"); + Serial.print("IRK (comma-separated hex): "); + Serial.println(irkString); + Serial.print("IRK (Base64 for Home Assistant Private BLE Device): "); + Serial.println(irkBase64); + Serial.print("IRK (reverse hex for Home Assistant ESPresense): "); + Serial.println(irkReverse); + Serial.println(); + } else { + Serial.println("!!! Failed to retrieve peer IRK !!!"); + Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key."); + Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n"); + } + + Serial.println("=======================================\n"); +} + +// Security callbacks to print IRKs once authentication completes +class MySecurityCallbacks : public BLESecurityCallbacks { +#if defined(CONFIG_BLUEDROID_ENABLED) + void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override { + // Print the IRK received by the peer + BLEAddress peerAddr(desc.bd_addr); + get_peer_irk(peerAddr); + } +#endif + +#if defined(CONFIG_NIMBLE_ENABLED) + void onAuthenticationComplete(ble_gap_conn_desc *desc) override { + // Print the IRK received by the peer + BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type); + get_peer_irk(peerAddr); + } +#endif +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + Serial.print("Using BLE stack: "); + Serial.println(BLEDevice::getBLEStackString()); + + BLEDevice::init("Secure BLE Server"); + + BLESecurity *pSecurity = new BLESecurity(); + + // Set security parameters + // Default parameters: + // - IO capability is set to NONE + // - Initiator and responder key distribution flags are set to both encryption and identity keys. + // - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it. + // - Key size is set to 16 bytes + + // Set static passkey + // The first argument defines if the passkey is static or random. + // The second argument is the passkey (ignored when using a random passkey). + pSecurity->setPassKey(true, SERVER_PIN); + + // Set IO capability to DisplayOnly + // We need the proper IO capability for MITM authentication even + // if the passkey is static and won't be shown to the user + // See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/ + pSecurity->setCapability(ESP_IO_CAP_OUT); + + // Set authentication mode + // Enable bonding, MITM (for password prompts), and secure connection for this example + pSecurity->setAuthenticationMode(true, true, true); + + // Set callbacks to handle authentication completion and print IRKs + BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); + + BLEServer *pServer = BLEDevice::createServer(); + pServer->advertiseOnDisconnect(true); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + uint32_t insecure_properties = BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE; + uint32_t secure_properties = insecure_properties; + + // NimBLE uses properties to secure characteristics. + // These special permission properties are not supported by Bluedroid and will be ignored. + // This can be removed if only using Bluedroid (ESP32). + // Check the BLECharacteristic.h file for more information. + secure_properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_WRITE_AUTHEN; + + BLECharacteristic *pSecureCharacteristic = pService->createCharacteristic(SECURE_CHARACTERISTIC_UUID, secure_properties); + BLECharacteristic *pInsecureCharacteristic = pService->createCharacteristic(INSECURE_CHARACTERISTIC_UUID, insecure_properties); + + // Bluedroid uses permissions to secure characteristics. + // This is the same as using the properties above. + // NimBLE does not use permissions and will ignore these calls. + // This can be removed if only using NimBLE (any SoC except ESP32). + pSecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); + pInsecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + + // Set value for secure characteristic + pSecureCharacteristic->setValue("Secure Hello World!"); + + // Set value for insecure characteristic + // When using NimBLE you will be able to read this characteristic without entering the passkey. + pInsecureCharacteristic->setValue("Insecure Hello World!"); + + pService->start(); + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + delay(2000); +} diff --git a/libraries/BLE/examples/Server_secure_static_passkey/ci.yml b/libraries/BLE/examples/Server_secure_static_passkey/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Server_secure_static_passkey/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/UART/ci.json b/libraries/BLE/examples/UART/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/UART/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/UART/ci.yml b/libraries/BLE/examples/UART/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/UART/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/Write/ci.json b/libraries/BLE/examples/Write/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/Write/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/Write/ci.yml b/libraries/BLE/examples/Write/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Write/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/examples/iBeacon/ci.json b/libraries/BLE/examples/iBeacon/ci.json deleted file mode 100644 index abe13a7ebbb..00000000000 --- a/libraries/BLE/examples/iBeacon/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} diff --git a/libraries/BLE/examples/iBeacon/ci.yml b/libraries/BLE/examples/iBeacon/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/iBeacon/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y diff --git a/libraries/BLE/library.properties b/libraries/BLE/library.properties index c6fae19a4a1..f4faf62a79e 100644 --- a/libraries/BLE/library.properties +++ b/libraries/BLE/library.properties @@ -1,5 +1,5 @@ name=BLE -version=3.3.0 +version=3.3.4 author=Neil Kolban maintainer=lucasssvaz sentence=BLE functions for ESP32 diff --git a/libraries/BLE/src/BLE2901.cpp b/libraries/BLE/src/BLE2901.cpp index 1fa4857ac33..20535dd91df 100644 --- a/libraries/BLE/src/BLE2901.cpp +++ b/libraries/BLE/src/BLE2901.cpp @@ -16,9 +16,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -47,7 +46,7 @@ BLE2901::BLE2901() : BLEDescriptor(BLEUUID((uint16_t)BLE2901_UUID)) {} /** * @brief Set the Characteristic User Description */ -void BLE2901::setDescription(String userDesc) { +void BLE2901::setDescription(const String &userDesc) { if (userDesc.length() > ESP_GATT_MAX_ATTR_LEN) { log_e("Size %d too large, must be no bigger than %d", userDesc.length(), ESP_GATT_MAX_ATTR_LEN); return; @@ -56,4 +55,4 @@ void BLE2901::setDescription(String userDesc) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2901.h b/libraries/BLE/src/BLE2901.h index 21e7cc9398c..949cad037fd 100644 --- a/libraries/BLE/src/BLE2901.h +++ b/libraries/BLE/src/BLE2901.h @@ -19,9 +19,8 @@ #define COMPONENTS_CPP_UTILS_BLE2901_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -40,9 +39,10 @@ class BLE2901 : public BLEDescriptor { ***************************************************************************/ BLE2901(); - void setDescription(String desc); + void setDescription(const String &desc); }; // BLE2901 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2901_H_ */ diff --git a/libraries/BLE/src/BLE2902.cpp b/libraries/BLE/src/BLE2902.cpp index 3bac9281328..464524431e2 100644 --- a/libraries/BLE/src/BLE2902.cpp +++ b/libraries/BLE/src/BLE2902.cpp @@ -15,9 +15,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -126,4 +125,4 @@ void BLE2902::setNotifications(bool flag) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2902.h b/libraries/BLE/src/BLE2902.h index 5cbf911ea4a..1dc685ca476 100644 --- a/libraries/BLE/src/BLE2902.h +++ b/libraries/BLE/src/BLE2902.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLE2902_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -56,11 +55,9 @@ This class will be removed in a future version.")]] BLE2902 : public BLEDescript bool getIndications(); void setNotifications(bool flag); void setIndications(bool flag); - - private: - friend class BLECharacteristic; }; // BLE2902 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ diff --git a/libraries/BLE/src/BLE2904.cpp b/libraries/BLE/src/BLE2904.cpp index 19658051120..2d3565ae0e3 100644 --- a/libraries/BLE/src/BLE2904.cpp +++ b/libraries/BLE/src/BLE2904.cpp @@ -13,10 +13,10 @@ * See also: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml */ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -85,4 +85,4 @@ void BLE2904::setUnit(uint16_t unit) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2904.h b/libraries/BLE/src/BLE2904.h index 87e8c03c048..f0f26763985 100644 --- a/libraries/BLE/src/BLE2904.h +++ b/libraries/BLE/src/BLE2904.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLE2904_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -95,8 +94,6 @@ class BLE2904 : public BLEDescriptor { void setUnit(uint16_t unit); private: - friend class BLECharacteristic; - /*************************************************************************** * Common private properties * ***************************************************************************/ @@ -105,5 +102,6 @@ class BLE2904 : public BLEDescriptor { }; // BLE2904 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ diff --git a/libraries/BLE/src/BLEAddress.cpp b/libraries/BLE/src/BLEAddress.cpp index 11c64850367..e345ac9fecc 100644 --- a/libraries/BLE/src/BLEAddress.cpp +++ b/libraries/BLE/src/BLEAddress.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -59,13 +58,8 @@ BLEAddress::BLEAddress() { * @param [in] otherAddress The other address to compare against. * @return True if the addresses are equal. */ -bool BLEAddress::equals(BLEAddress otherAddress) { -#if defined(CONFIG_NIMBLE_ENABLED) - if (m_addrType != otherAddress.m_addrType) { - return false; - } -#endif - return memcmp(otherAddress.getNative(), m_address, ESP_BD_ADDR_LEN) == 0; +bool BLEAddress::equals(const BLEAddress &otherAddress) const { + return *this == otherAddress; } bool BLEAddress::operator==(const BLEAddress &otherAddress) const { @@ -116,9 +110,9 @@ uint8_t *BLEAddress::getNative() { * * @return The string representation of the address. */ -String BLEAddress::toString() { - auto size = 18; - char *res = (char *)malloc(size); +String BLEAddress::toString() const { + constexpr size_t size = 18; + char res[size]; #if defined(CONFIG_BLUEDROID_ENABLED) snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]); @@ -129,7 +123,6 @@ String BLEAddress::toString() { #endif String ret(res); - free(res); return ret; } @@ -158,7 +151,7 @@ BLEAddress::BLEAddress(esp_bd_addr_t address) { * * @param [in] stringAddress The hex representation of the address. */ -BLEAddress::BLEAddress(String stringAddress) { +BLEAddress::BLEAddress(const String &stringAddress) { if (stringAddress.length() != 17) { return; } @@ -193,7 +186,7 @@ BLEAddress::BLEAddress(ble_addr_t address) { m_addrType = address.type; } -uint8_t BLEAddress::getType() { +uint8_t BLEAddress::getType() const { return m_addrType; } @@ -209,7 +202,7 @@ uint8_t BLEAddress::getType() { * @param [in] stringAddress The hex representation of the address. * @param [in] type The address type. */ -BLEAddress::BLEAddress(String stringAddress, uint8_t type) { +BLEAddress::BLEAddress(const String &stringAddress, uint8_t type) { if (stringAddress.length() != 17) { return; } @@ -227,4 +220,4 @@ BLEAddress::BLEAddress(String stringAddress, uint8_t type) { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAddress.h b/libraries/BLE/src/BLEAddress.h index 71b3bdfce5f..9b6bd422bad 100644 --- a/libraries/BLE/src/BLEAddress.h +++ b/libraries/BLE/src/BLEAddress.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_ #define COMPONENTS_CPP_UTILS_BLEADDRESS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -22,7 +22,9 @@ ***************************************************************************/ #include "WString.h" +#if SOC_BLE_SUPPORTED #include +#endif #include /*************************************************************************** @@ -62,7 +64,7 @@ class BLEAddress { ***************************************************************************/ BLEAddress(); - bool equals(BLEAddress otherAddress); + bool equals(const BLEAddress &otherAddress) const; bool operator==(const BLEAddress &otherAddress) const; bool operator!=(const BLEAddress &otherAddress) const; bool operator<(const BLEAddress &otherAddress) const; @@ -70,7 +72,7 @@ class BLEAddress { bool operator>(const BLEAddress &otherAddress) const; bool operator>=(const BLEAddress &otherAddress) const; uint8_t *getNative(); - String toString(); + String toString() const; /*************************************************************************** * Bluedroid public declarations * @@ -78,7 +80,7 @@ class BLEAddress { #if defined(CONFIG_BLUEDROID_ENABLED) BLEAddress(esp_bd_addr_t address); - BLEAddress(String stringAddress); + BLEAddress(const String &stringAddress); #endif /*************************************************************************** @@ -87,9 +89,9 @@ class BLEAddress { #if defined(CONFIG_NIMBLE_ENABLED) BLEAddress(ble_addr_t address); - BLEAddress(String stringAddress, uint8_t type = BLE_ADDR_PUBLIC); + BLEAddress(const String &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); BLEAddress(uint8_t address[ESP_BD_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC); - uint8_t getType(); + uint8_t getType() const; #endif private: @@ -109,5 +111,6 @@ class BLEAddress { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */ diff --git a/libraries/BLE/src/BLEAdvertisedDevice.cpp b/libraries/BLE/src/BLEAdvertisedDevice.cpp index 3ac1dfab5c8..40ff78c6a7d 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.cpp +++ b/libraries/BLE/src/BLEAdvertisedDevice.cpp @@ -16,8 +16,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -46,6 +46,8 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() { m_txPower = 0; m_pScan = nullptr; m_advType = 0; + m_payload = nullptr; + m_payloadLength = 0; #if defined(CONFIG_NIMBLE_ENABLED) m_callbackSent = false; @@ -59,6 +61,110 @@ BLEAdvertisedDevice::BLEAdvertisedDevice() { m_isLegacyAdv = true; } // BLEAdvertisedDevice +BLEAdvertisedDevice::~BLEAdvertisedDevice() { + if (m_payload != nullptr) { + free(m_payload); + m_payload = nullptr; + m_payloadLength = 0; + } +} // ~BLEAdvertisedDevice + +BLEAdvertisedDevice::BLEAdvertisedDevice(const BLEAdvertisedDevice &other) { + m_adFlag = other.m_adFlag; + m_appearance = other.m_appearance; + m_deviceType = other.m_deviceType; + m_manufacturerData = other.m_manufacturerData; + m_name = other.m_name; + m_rssi = other.m_rssi; + m_serviceUUIDs = other.m_serviceUUIDs; + m_serviceData = other.m_serviceData; + m_serviceDataUUIDs = other.m_serviceDataUUIDs; + m_txPower = other.m_txPower; + m_pScan = other.m_pScan; + m_advType = other.m_advType; + m_address = other.m_address; + m_addressType = other.m_addressType; + +#if defined(CONFIG_NIMBLE_ENABLED) + m_callbackSent = other.m_callbackSent; +#endif + + m_haveAppearance = other.m_haveAppearance; + m_haveManufacturerData = other.m_haveManufacturerData; + m_haveName = other.m_haveName; + m_haveRSSI = other.m_haveRSSI; + m_haveTXPower = other.m_haveTXPower; + m_isLegacyAdv = other.m_isLegacyAdv; + + // Deep copy the payload + m_payloadLength = other.m_payloadLength; + if (other.m_payload != nullptr && other.m_payloadLength > 0) { + m_payload = (uint8_t *)malloc(m_payloadLength); + if (m_payload != nullptr) { + memcpy(m_payload, other.m_payload, m_payloadLength); + } else { + log_e("Failed to allocate %zu bytes for payload in copy constructor", m_payloadLength); + m_payloadLength = 0; + } + } else { + m_payload = nullptr; + m_payloadLength = 0; + } +} // BLEAdvertisedDevice copy constructor + +BLEAdvertisedDevice &BLEAdvertisedDevice::operator=(const BLEAdvertisedDevice &other) { + if (this == &other) { + return *this; + } + + m_adFlag = other.m_adFlag; + m_appearance = other.m_appearance; + m_deviceType = other.m_deviceType; + m_manufacturerData = other.m_manufacturerData; + m_name = other.m_name; + m_rssi = other.m_rssi; + m_serviceUUIDs = other.m_serviceUUIDs; + m_serviceData = other.m_serviceData; + m_serviceDataUUIDs = other.m_serviceDataUUIDs; + m_txPower = other.m_txPower; + m_pScan = other.m_pScan; + m_advType = other.m_advType; + m_address = other.m_address; + m_addressType = other.m_addressType; + +#if defined(CONFIG_NIMBLE_ENABLED) + m_callbackSent = other.m_callbackSent; +#endif + + m_haveAppearance = other.m_haveAppearance; + m_haveManufacturerData = other.m_haveManufacturerData; + m_haveName = other.m_haveName; + m_haveRSSI = other.m_haveRSSI; + m_haveTXPower = other.m_haveTXPower; + m_isLegacyAdv = other.m_isLegacyAdv; + + // Free existing payload and deep copy the new one + if (m_payload != nullptr) { + free(m_payload); + } + + m_payloadLength = other.m_payloadLength; + if (other.m_payload != nullptr && other.m_payloadLength > 0) { + m_payload = (uint8_t *)malloc(m_payloadLength); + if (m_payload != nullptr) { + memcpy(m_payload, other.m_payload, m_payloadLength); + } else { + log_e("Failed to allocate %zu bytes for payload in assignment operator", m_payloadLength); + m_payloadLength = 0; + } + } else { + m_payload = nullptr; + m_payloadLength = 0; + } + + return *this; +} // BLEAdvertisedDevice assignment operator + bool BLEAdvertisedDevice::isLegacyAdvertisement() { return m_isLegacyAdv; } @@ -308,8 +414,30 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len) uint8_t ad_type; uint8_t sizeConsumed = 0; bool finished = false; - m_payload = payload; - m_payloadLength = total_len; + + // Store/append raw payload data for later retrieval + // This handles both ADV and Scan Response packets by merging them + if (m_payload != nullptr && m_payloadLength > 0) { + // Append new payload data (scan response) to existing (advertisement) + uint8_t *new_payload = (uint8_t *)realloc(m_payload, m_payloadLength + total_len); + if (new_payload != nullptr) { + memcpy(new_payload + m_payloadLength, payload, total_len); + m_payload = new_payload; + m_payloadLength += total_len; + } else { + log_e("Failed to reallocate %zu bytes for payload (append)", m_payloadLength + total_len); + } + } else { + // First payload - make a copy since the original buffer may be reused + m_payload = (uint8_t *)malloc(total_len); + if (m_payload != nullptr) { + memcpy(m_payload, payload, total_len); + m_payloadLength = total_len; + } else { + log_e("Failed to allocate %zu bytes for payload", total_len); + m_payloadLength = 0; + } + } while (!finished) { length = *payload; // Retrieve the length of the record. @@ -446,21 +574,38 @@ void BLEAdvertisedDevice::parseAdvertisement(uint8_t *payload, size_t total_len) } // parseAdvertisement /** - * @brief Parse the advertising payload. + * @brief Set the advertising payload. * @param [in] payload The payload of the advertised device. * @param [in] total_len The length of payload + * @param [in] append If true, append to existing payload (for scan response merging) */ void BLEAdvertisedDevice::setPayload(uint8_t *payload, size_t total_len, bool append) { - if (m_payload == nullptr || m_payloadLength == 0) { + if (total_len == 0 || payload == nullptr) { return; } - if (append) { - m_payload = (uint8_t *)realloc(m_payload, m_payloadLength + total_len); - memcpy(m_payload + m_payloadLength, payload, total_len); + if (append && m_payload != nullptr && m_payloadLength > 0) { + // Append scan response data to existing advertisement data + uint8_t *new_payload = (uint8_t *)realloc(m_payload, m_payloadLength + total_len); + if (new_payload == nullptr) { + log_e("Failed to reallocate %zu bytes for payload buffer", m_payloadLength + total_len); + return; + } + memcpy(new_payload + m_payloadLength, payload, total_len); + m_payload = new_payload; m_payloadLength += total_len; } else { - m_payload = payload; + // First payload or replacing existing - make a copy + if (m_payload != nullptr && m_payloadLength > 0) { + free(m_payload); + } + m_payload = (uint8_t *)malloc(total_len); + if (m_payload == nullptr) { + log_e("Failed to allocate %zu bytes for payload buffer", total_len); + m_payloadLength = 0; + return; + } + memcpy(m_payload, payload, total_len); m_payloadLength = total_len; } } // setPayload @@ -605,7 +750,7 @@ String BLEAdvertisedDevice::toString() { res += val; } if (haveRSSI()) { - char val[4]; + char val[5]; snprintf(val, sizeof(val), "%i", getRSSI()); res += ", rssi: "; res += val; @@ -659,4 +804,4 @@ uint8_t BLEAdvertisedDevice::getAdvType() { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAdvertisedDevice.h b/libraries/BLE/src/BLEAdvertisedDevice.h index 2a2fc0c81aa..2a34e5a43ca 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.h +++ b/libraries/BLE/src/BLEAdvertisedDevice.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ #define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -74,6 +74,9 @@ class BLEAdvertisedDevice { ***************************************************************************/ BLEAdvertisedDevice(); + ~BLEAdvertisedDevice(); + BLEAdvertisedDevice(const BLEAdvertisedDevice &other); + BLEAdvertisedDevice &operator=(const BLEAdvertisedDevice &other); BLEAddress getAddress(); uint16_t getAppearance(); String getManufacturerData(); @@ -230,5 +233,6 @@ class BLEExtAdvertisingCallbacks { #endif // SOC_BLE_50_SUPPORTED && CONFIG_BLUEDROID_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */ diff --git a/libraries/BLE/src/BLEAdvertising.cpp b/libraries/BLE/src/BLEAdvertising.cpp index 4e76873dcad..41350c67487 100644 --- a/libraries/BLE/src/BLEAdvertising.cpp +++ b/libraries/BLE/src/BLEAdvertising.cpp @@ -22,9 +22,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -1425,4 +1424,4 @@ bool BLEAdvertising::stop() { #endif /* CONFIG_NIMBLE_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAdvertising.h b/libraries/BLE/src/BLEAdvertising.h index cf68768ec91..2ea112bbdc9 100644 --- a/libraries/BLE/src/BLEAdvertising.h +++ b/libraries/BLE/src/BLEAdvertising.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ #define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -253,5 +253,6 @@ class BLEMultiAdvertising { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/libraries/BLE/src/BLEBeacon.cpp b/libraries/BLE/src/BLEBeacon.cpp index 0a6c6b05258..0b10836b121 100644 --- a/libraries/BLE/src/BLEBeacon.cpp +++ b/libraries/BLE/src/BLEBeacon.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -62,7 +61,7 @@ int8_t BLEBeacon::getSignalPower() { return m_beaconData.signalPower; } -void BLEBeacon::setData(String data) { +void BLEBeacon::setData(const String &data) { if (data.length() != sizeof(m_beaconData)) { log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); return; @@ -96,4 +95,4 @@ void BLEBeacon::setProximityUUID(BLEUUID uuid) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEBeacon.h b/libraries/BLE/src/BLEBeacon.h index e0eaddda6e4..b3f3408a150 100644 --- a/libraries/BLE/src/BLEBeacon.h +++ b/libraries/BLE/src/BLEBeacon.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ #define COMPONENTS_CPP_UTILS_BLEBEACON_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -55,7 +55,7 @@ class BLEBeacon { uint16_t getManufacturerId(); BLEUUID getProximityUUID(); int8_t getSignalPower(); - void setData(String data); + void setData(const String &data); void setMajor(uint16_t major); void setMinor(uint16_t minor); void setManufacturerId(uint16_t manufacturerId); @@ -64,5 +64,6 @@ class BLEBeacon { }; // BLEBeacon #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp index 0234cd11cca..cb971796b31 100644 --- a/libraries/BLE/src/BLECharacteristic.cpp +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -124,7 +123,7 @@ void BLECharacteristic::addDescriptor(BLEDescriptor *pDescriptor) { * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ -BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID) { +BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID) const { return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); } // getDescriptorByUUID @@ -133,7 +132,7 @@ BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ -BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { +BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) const { return m_descriptorMap.getByUUID(descriptorUUID); } // getDescriptorByUUID @@ -141,24 +140,24 @@ BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { * @brief Get the handle of the characteristic. * @return The handle of the characteristic. */ -uint16_t BLECharacteristic::getHandle() { +uint16_t BLECharacteristic::getHandle() const { return m_handle; } // getHandle -void BLECharacteristic::setAccessPermissions(uint8_t perm) { +void BLECharacteristic::setAccessPermissions(uint16_t perm) { #ifdef CONFIG_BLUEDROID_ENABLED m_permissions = perm; #endif } -esp_gatt_char_prop_t BLECharacteristic::getProperties() { +esp_gatt_char_prop_t BLECharacteristic::getProperties() const { return m_properties; } // getProperties /** * @brief Get the service associated with this characteristic. */ -BLEService *BLECharacteristic::getService() { +BLEService *BLECharacteristic::getService() const { return m_pService; } // getService @@ -166,7 +165,7 @@ BLEService *BLECharacteristic::getService() { * @brief Get the UUID of the characteristic. * @return The UUID of the characteristic. */ -BLEUUID BLECharacteristic::getUUID() { +BLEUUID BLECharacteristic::getUUID() const { return m_bleUUID; } // getUUID @@ -174,7 +173,7 @@ BLEUUID BLECharacteristic::getUUID() { * @brief Retrieve the current value of the characteristic. * @return A pointer to storage containing the current characteristic value. */ -String BLECharacteristic::getValue() { +String BLECharacteristic::getValue() const { return m_value.getValue(); } // getValue @@ -190,7 +189,7 @@ uint8_t *BLECharacteristic::getData() { * @brief Retrieve the current length of the data of the characteristic. * @return Amount of databytes of the characteristic. */ -size_t BLECharacteristic::getLength() { +size_t BLECharacteristic::getLength() const { return m_value.getLength(); } // getLength @@ -346,7 +345,7 @@ void BLECharacteristic::setReadProperty(bool value) { * @param [in] data The data to set for the characteristic. * @param [in] length The length of the data in bytes. */ -void BLECharacteristic::setValue(uint8_t *data, size_t length) { +void BLECharacteristic::setValue(const uint8_t *data, size_t length) { // The call to BLEUtils::buildHexData() doesn't output anything if the log level is not // "VERBOSE". As it is quite CPU intensive, it is much better to not call it if not needed. #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE @@ -371,43 +370,28 @@ void BLECharacteristic::setValue(uint8_t *data, size_t length) { * @param [in] Set the value of the characteristic. * @return N/A. */ -void BLECharacteristic::setValue(String value) { - setValue((uint8_t *)(value.c_str()), value.length()); +void BLECharacteristic::setValue(const String &value) { + setValue(reinterpret_cast(value.c_str()), value.length()); } // setValue -void BLECharacteristic::setValue(uint16_t &data16) { - uint8_t temp[2]; - temp[0] = data16; - temp[1] = data16 >> 8; - setValue(temp, 2); +void BLECharacteristic::setValue(uint16_t data16) { + setValue(reinterpret_cast(&data16), sizeof(data16)); } // setValue -void BLECharacteristic::setValue(uint32_t &data32) { - uint8_t temp[4]; - temp[0] = data32; - temp[1] = data32 >> 8; - temp[2] = data32 >> 16; - temp[3] = data32 >> 24; - setValue(temp, 4); +void BLECharacteristic::setValue(uint32_t data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(int &data32) { - uint8_t temp[4]; - temp[0] = data32; - temp[1] = data32 >> 8; - temp[2] = data32 >> 16; - temp[3] = data32 >> 24; - setValue(temp, 4); +void BLECharacteristic::setValue(int data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(float &data32) { - float temp = data32; - setValue((uint8_t *)&temp, 4); +void BLECharacteristic::setValue(float data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(double &data64) { - double temp = data64; - setValue((uint8_t *)&temp, 8); +void BLECharacteristic::setValue(double data64) { + setValue(reinterpret_cast(&data64), sizeof(data64)); } // setValue /** @@ -440,7 +424,7 @@ void BLECharacteristic::setWriteProperty(bool value) { * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. */ -String BLECharacteristic::toString() { +String BLECharacteristic::toString() const { String res = "UUID: " + m_bleUUID.toString() + ", handle : 0x"; char hex[5]; snprintf(hex, sizeof(hex), "%04x", m_handle); @@ -467,7 +451,7 @@ String BLECharacteristic::toString() { return res; } // toString -BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} +BLECharacteristicCallbacks::~BLECharacteristicCallbacks() = default; // Common callbacks void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { @@ -582,6 +566,32 @@ void BLECharacteristic::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_ga // we save the new value. Next we look at the need_rsp flag which indicates whether or not we need // to send a response. If we do, then we formulate a response and send it. if (param->write.handle == m_handle) { + + // Check for authorization requirement + if (m_permissions & ESP_GATT_PERM_WRITE_AUTHORIZATION) { + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Authorization required for write operation. Checking authorization..."); + authorized = BLEDevice::m_securityCallbacks->onAuthorizationRequest(param->write.conn_id, m_handle, false); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting write authorization request"); + } + + if (!authorized) { + log_i("Write authorization rejected"); + if (param->write.need_rsp) { + esp_err_t errRc = ::esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_INSUF_AUTHORIZATION, nullptr); + if (errRc != ESP_OK) { + log_e("esp_ble_gatts_send_response (authorization failed): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + return; // Exit early, don't process the write + } else { + log_i("Write authorization granted"); + } + } + if (param->write.is_prep) { m_value.addPart(param->write.value, param->write.len); m_writeEvt = true; @@ -637,6 +647,31 @@ void BLECharacteristic::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_ga { if (param->read.handle == m_handle) { + // Check for authorization requirement + if (m_permissions & ESP_GATT_PERM_READ_AUTHORIZATION) { + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Authorization required for read operation. Checking authorization..."); + authorized = BLEDevice::m_securityCallbacks->onAuthorizationRequest(param->read.conn_id, m_handle, true); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting read authorization request"); + } + + if (!authorized) { + log_i("Read authorization rejected"); + if (param->read.need_rsp) { + esp_err_t errRc = ::esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_INSUF_AUTHORIZATION, nullptr); + if (errRc != ESP_OK) { + log_e("esp_ble_gatts_send_response (authorization failed): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + return; // Exit early, don't process the read + } else { + log_i("Read authorization granted"); + } + } + // Here's an interesting thing. The read request has the option of saying whether we need a response // or not. What would it "mean" to receive a read request and NOT send a response back? That feels like // a very strange read. @@ -869,6 +904,52 @@ void BLECharacteristicCallbacks::onWrite(BLECharacteristic *pCharacteristic, esp #if defined(CONFIG_NIMBLE_ENABLED) +/** + * @brief Process a deferred write callback. + * + * This function is called as a FreeRTOS task to execute the onWrite callback + * after the write response has been sent to the client. This maintains backwards + * compatibility with Bluedroid, where the write response is sent before the + * onWrite callback is invoked. + * + * The delay is based on the connection interval to ensure the write response + * packet has been transmitted over the air before the callback executes. + * + * See: https://github.com/espressif/arduino-esp32/issues/11938 + */ +void BLECharacteristic::processDeferredWriteCallback(void *pvParameters) { + DeferredWriteCallback *pCallback = (DeferredWriteCallback *)pvParameters; + + // Get connection parameters to calculate appropriate delay + ble_gap_conn_desc desc; + int rc = ble_gap_conn_find(pCallback->conn_handle, &desc); + + if (rc == 0) { + // Connection interval is in units of 1.25ms + // Wait for at least one connection interval to ensure the write response + // has been transmitted. Add a small buffer for processing. + uint16_t intervalMs = (desc.conn_itvl * 125) / 100; // Convert to milliseconds + uint16_t delayMs = intervalMs + 5; // Add 5ms buffer + + log_v("Deferring write callback by %dms (conn_interval=%d units, %dms)", delayMs, desc.conn_itvl, intervalMs); + vTaskDelay(pdMS_TO_TICKS(delayMs)); + } else { + // If we can't get connection parameters, use a conservative default + // Most connections use 7.5-30ms intervals, so 50ms should be safe + log_w("Could not get connection parameters, using default 50ms delay"); + vTaskDelay(pdMS_TO_TICKS(50)); + } + + // Call the onWrite callback now that the response has been transmitted + pCallback->pCharacteristic->m_pCallbacks->onWrite(pCallback->pCharacteristic, &pCallback->desc); + + // Free the allocated memory + delete pCallback; + + // Delete this one-shot task + vTaskDelete(NULL); +} + int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg) { const ble_uuid_t *uuid; int rc; @@ -887,7 +968,6 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr if (ctxt->om->om_pkthdr_len > 8) { rc = ble_gap_conn_find(conn_handle, &desc); assert(rc == 0); - pCharacteristic->m_pCallbacks->onRead(pCharacteristic); pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc); } @@ -921,8 +1001,28 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr rc = ble_gap_conn_find(conn_handle, &desc); assert(rc == 0); pCharacteristic->setValue(buf, len); - pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); - pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc); + + // Defer the onWrite callback to maintain backwards compatibility with Bluedroid. + // In Bluedroid, the write response is sent BEFORE the onWrite callback is invoked. + // In NimBLE, the response is sent implicitly when this function returns. + // By deferring the callback to a separate task with a delay based on the connection + // interval, we ensure the response packet is transmitted before the callback executes. + // See: https://github.com/espressif/arduino-esp32/issues/11938 + DeferredWriteCallback *pCallback = new DeferredWriteCallback(); + pCallback->pCharacteristic = pCharacteristic; + pCallback->desc = desc; + pCallback->conn_handle = conn_handle; + + // Create a one-shot task to execute the callback after the response is transmitted + // Using priority 1 (low priority) and sufficient stack for callback operations + // Note: Stack must be large enough to handle notify() calls from within onWrite() + xTaskCreate( + processDeferredWriteCallback, "BLEWriteCB", + 4096, // Stack size - increased to handle notify() operations + pCallback, + 1, // Priority (low) + NULL // Task handle (not needed for one-shot task) + ); return 0; } @@ -1136,4 +1236,4 @@ void BLECharacteristicCallbacks::onSubscribe(BLECharacteristic *pCharacteristic, #endif /* CONFIG_NIMBLE_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLECharacteristic.h b/libraries/BLE/src/BLECharacteristic.h index 27df5a30c3e..f7f61cde313 100644 --- a/libraries/BLE/src/BLECharacteristic.h +++ b/libraries/BLE/src/BLECharacteristic.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ #define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -47,6 +47,8 @@ #include #include #include "BLEConnInfo.h" +#include +#include #define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN #define ESP_GATT_CHAR_PROP_BIT_READ BLE_GATT_CHR_PROP_READ #define ESP_GATT_CHAR_PROP_BIT_WRITE BLE_GATT_CHR_PROP_WRITE @@ -62,7 +64,7 @@ #if defined(CONFIG_NIMBLE_ENABLED) typedef uint16_t esp_gatt_char_prop_t; -typedef uint8_t esp_gatt_perm_t; +typedef uint16_t esp_gatt_perm_t; #endif /*************************************************************************** @@ -85,13 +87,13 @@ class BLEDescriptorMap { void setByUUID(const char *uuid, BLEDescriptor *pDescriptor); void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); - BLEDescriptor *getByUUID(const char *uuid); - BLEDescriptor *getByUUID(BLEUUID uuid); - BLEDescriptor *getByHandle(uint16_t handle); - String toString(); + BLEDescriptor *getByUUID(const char *uuid) const; + BLEDescriptor *getByUUID(BLEUUID uuid) const; + BLEDescriptor *getByHandle(uint16_t handle) const; + String toString() const; BLEDescriptor *getFirst(); BLEDescriptor *getNext(); - int getRegisteredDescriptorCount(); + int getRegisteredDescriptorCount() const; void removeDescriptor(BLEDescriptor *pDescriptor); /*************************************************************************** @@ -140,11 +142,17 @@ class BLECharacteristic { #if defined(CONFIG_BLUEDROID_ENABLED) static const uint32_t PROPERTY_READ = 1 << 0; + static const uint32_t PROPERTY_READ_ENC = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_READ_AUTHEN = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_READ_AUTHOR = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. static const uint32_t PROPERTY_WRITE = 1 << 1; + static const uint32_t PROPERTY_WRITE_NR = 1 << 5; + static const uint32_t PROPERTY_WRITE_ENC = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_WRITE_AUTHEN = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_WRITE_AUTHOR = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. static const uint32_t PROPERTY_NOTIFY = 1 << 2; static const uint32_t PROPERTY_BROADCAST = 1 << 3; static const uint32_t PROPERTY_INDICATE = 1 << 4; - static const uint32_t PROPERTY_WRITE_NR = 1 << 5; #endif /*************************************************************************** @@ -161,8 +169,8 @@ class BLECharacteristic { static const uint32_t PROPERTY_WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC; static const uint32_t PROPERTY_WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN; static const uint32_t PROPERTY_WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR; - static const uint32_t PROPERTY_BROADCAST = BLE_GATT_CHR_F_BROADCAST; static const uint32_t PROPERTY_NOTIFY = BLE_GATT_CHR_F_NOTIFY; + static const uint32_t PROPERTY_BROADCAST = BLE_GATT_CHR_F_BROADCAST; static const uint32_t PROPERTY_INDICATE = BLE_GATT_CHR_F_INDICATE; #endif @@ -175,26 +183,26 @@ class BLECharacteristic { virtual ~BLECharacteristic(); void addDescriptor(BLEDescriptor *pDescriptor); - BLEDescriptor *getDescriptorByUUID(const char *descriptorUUID); - BLEDescriptor *getDescriptorByUUID(BLEUUID descriptorUUID); - BLEUUID getUUID(); - String getValue(); + BLEDescriptor *getDescriptorByUUID(const char *descriptorUUID) const; + BLEDescriptor *getDescriptorByUUID(BLEUUID descriptorUUID) const; + BLEUUID getUUID() const; + String getValue() const; uint8_t *getData(); - size_t getLength(); + size_t getLength() const; void indicate(); void notify(bool is_notification = true); void setCallbacks(BLECharacteristicCallbacks *pCallbacks); - void setValue(uint8_t *data, size_t size); - void setValue(String value); - void setValue(uint16_t &data16); - void setValue(uint32_t &data32); - void setValue(int &data32); - void setValue(float &data32); - void setValue(double &data64); - String toString(); - uint16_t getHandle(); - void setAccessPermissions(uint8_t perm); - esp_gatt_char_prop_t getProperties(); + void setValue(const uint8_t *data, size_t size); + void setValue(const String &value); + void setValue(uint16_t data16); + void setValue(uint32_t data32); + void setValue(int data32); + void setValue(float data32); + void setValue(double data64); + String toString() const; + uint16_t getHandle() const; + void setAccessPermissions(uint16_t perm); + esp_gatt_char_prop_t getProperties() const; void setReadProperty(bool value); void setWriteProperty(bool value); void setNotifyProperty(bool value); @@ -240,6 +248,13 @@ class BLECharacteristic { portMUX_TYPE m_readMux; uint8_t m_removed; std::vector> m_subscribedVec; + + // Deferred callback support for maintaining backwards compatibility with Bluedroid timing + struct DeferredWriteCallback { + BLECharacteristic *pCharacteristic; + ble_gap_conn_desc desc; + uint16_t conn_handle; + }; #endif /*************************************************************************** @@ -247,7 +262,7 @@ class BLECharacteristic { ***************************************************************************/ void executeCreate(BLEService *pService); - BLEService *getService(); + BLEService *getService() const; void setHandle(uint16_t handle); /*************************************************************************** @@ -265,6 +280,7 @@ class BLECharacteristic { #if defined(CONFIG_NIMBLE_ENABLED) void setSubscribe(struct ble_gap_event *event); static int handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_handle, struct ble_gatt_access_ctxt *ctxt, void *arg); + static void processDeferredWriteCallback(void *pvParameters); #endif }; // BLECharacteristic @@ -324,5 +340,6 @@ class BLECharacteristicCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ diff --git a/libraries/BLE/src/BLECharacteristicMap.cpp b/libraries/BLE/src/BLECharacteristicMap.cpp index ec3588bcde4..f82f5be47bf 100644 --- a/libraries/BLE/src/BLECharacteristicMap.cpp +++ b/libraries/BLE/src/BLECharacteristicMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -36,7 +35,7 @@ * @param [in] handle The handle to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) { +BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) const { return m_handleMap.at(handle); } // getByHandle @@ -45,7 +44,7 @@ BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) { * @param [in] UUID The UUID to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) { +BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) const { return getByUUID(BLEUUID(uuid)); } @@ -54,7 +53,7 @@ BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) { * @param [in] UUID The UUID to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByUUID(BLEUUID uuid) { +BLECharacteristic *BLECharacteristicMap::getByUUID(BLEUUID uuid) const { for (auto &myPair : m_uuidMap) { if (myPair.first->getUUID().equals(uuid)) { return myPair.first; @@ -95,7 +94,7 @@ BLECharacteristic *BLECharacteristicMap::getNext() { * @brief Get the number of registered characteristics. * @return The number of registered characteristics. */ -int BLECharacteristicMap::getRegisteredCharacteristicCount() { +int BLECharacteristicMap::getRegisteredCharacteristicCount() const { return m_uuidMap.size(); } // getRegisteredCharacteristicCount @@ -133,7 +132,7 @@ void BLECharacteristicMap::setByUUID(BLECharacteristic *pCharacteristic, BLEUUID * @brief Return a string representation of the characteristic map. * @return A string representation of the characteristic map. */ -String BLECharacteristicMap::toString() { +String BLECharacteristicMap::toString() const { String res; int count = 0; char hex[5]; @@ -183,4 +182,4 @@ void BLECharacteristicMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEClient.cpp b/libraries/BLE/src/BLEClient.cpp index c101993d002..808614ba5bf 100644 --- a/libraries/BLE/src/BLEClient.cpp +++ b/libraries/BLE/src/BLEClient.cpp @@ -10,16 +10,18 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#include "Arduino.h" +#if SOC_BLE_SUPPORTED #include +#endif #include "BLEClient.h" #include "BLEUtils.h" #include "BLEService.h" @@ -29,6 +31,7 @@ #include #include "BLEDevice.h" #include "esp32-hal-log.h" +#include "BLESecurity.h" /*************************************************************************** * Bluedroid includes * @@ -534,6 +537,8 @@ void BLEClient::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t m_semaphoreRssiCmplEvt.give(); m_semaphoreSearchCmplEvt.give(1); BLEDevice::removePeerDevice(m_appId, true); + // Reset security state on disconnect + BLESecurity::resetSecurity(); if (m_wasConnected && m_pClientCallbacks != nullptr) { m_pClientCallbacks->onDisconnect(this); } @@ -598,11 +603,11 @@ void BLEClient::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t if (errRc != ESP_OK) { log_e("esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel); + // Set encryption on connect for BlueDroid when security is enabled + // This ensures security is established before any secure operations + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(evtParam->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTC_CONNECT_EVT @@ -979,6 +984,8 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { BLEDevice::removePeerDevice(client->m_appId, true); client->m_isConnected = false; + // Reset security state on disconnect + BLESecurity::resetSecurity(); if (client->m_pClientCallbacks != nullptr) { client->m_pClientCallbacks->onDisconnect(client); } @@ -1006,6 +1013,10 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { break; } + if (BLESecurity::m_securityEnabled) { + BLESecurity::startSecurity(client->m_conn_id); + } + // In the case of a multiconnecting device we ignore this device when // scanning since we are already connected to it // BLEDevice::addIgnored(client->m_peerAddress); @@ -1136,8 +1147,6 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { ble_store_util_delete_peer(&desc.peer_id_addr); } else if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); - } else { - client->m_pClientCallbacks->onAuthenticationComplete(&desc); } } @@ -1164,27 +1173,52 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { } if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + // Display the passkey on this device + log_d("BLE_SM_IOACT_DISP"); + pkey.action = event->passkey.params.action; - pkey.passkey = BLESecurity::m_passkey; // This is the passkey to be entered on peer + pkey.passkey = BLESecurity::getPassKey(); // This is the passkey to be entered on peer + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); + } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); + } + + if (BLEDevice::m_securityCallbacks != nullptr) { + BLEDevice::m_securityCallbacks->onPassKeyNotify(pkey.passkey); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + // Check if the passkey on the peer device is correct + log_d("BLE_SM_IOACT_NUMCMP"); + log_d("Passkey on device's display: %d", event->passkey.params.numcmp); pkey.action = event->passkey.params.action; - // Compatibility only - Do not use, should be removed the in future + if (BLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = BLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); - //////////////////////////////////////////////////// } else { - pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); + log_e("onConfirmPIN not implemented. Rejecting connection"); + pkey.numcmp_accept = 0; } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); - //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + // Out of band pairing + // TODO: Handle out of band pairing + log_w("BLE_SM_IOACT_OOB: Not implemented"); + static uint8_t tem_oob[16] = {0}; pkey.action = event->passkey.params.action; for (int i = 0; i < 16; i++) { @@ -1192,24 +1226,35 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); - //////// } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { - log_d("Enter the passkey"); + // Input passkey from peer device + log_d("BLE_SM_IOACT_INPUT"); + pkey.action = event->passkey.params.action; + pkey.passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } + } - // Compatibility only - Do not use, should be removed the in future - if (BLEDevice::m_securityCallbacks != nullptr) { - pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); - ///////////////////////////////////////////// + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); } else { - pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); + log_i("Passkey: %d", pkey.passkey); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { - log_d("No passkey action required"); + log_d("BLE_SM_IOACT_NONE"); + log_i("No passkey action required"); } return 0; @@ -1255,21 +1300,7 @@ bool BLEClientCallbacks::onConnParamsUpdateRequest(BLEClient *pClient, const ble return true; } -uint32_t BLEClientCallbacks::onPassKeyRequest() { - log_d("onPassKeyRequest: default: 123456"); - return 123456; -} - -void BLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc *desc) { - log_d("onAuthenticationComplete: default"); -} - -bool BLEClientCallbacks::onConfirmPIN(uint32_t pin) { - log_d("onConfirmPIN: default: true"); - return true; -} - #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEClient.h b/libraries/BLE/src/BLEClient.h index 97ef72f4917..cbe0bde7e75 100644 --- a/libraries/BLE/src/BLEClient.h +++ b/libraries/BLE/src/BLEClient.h @@ -13,9 +13,8 @@ #define MAIN_BLECLIENT_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -209,12 +208,10 @@ class BLEClientCallbacks { #if defined(CONFIG_NIMBLE_ENABLED) virtual bool onConnParamsUpdateRequest(BLEClient *pClient, const ble_gap_upd_params *params); - virtual uint32_t onPassKeyRequest(); - virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); - virtual bool onConfirmPIN(uint32_t pin); #endif }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* MAIN_BLECLIENT_H_ */ diff --git a/libraries/BLE/src/BLEDescriptor.cpp b/libraries/BLE/src/BLEDescriptor.cpp index 51c77bb9b49..fe5212b6f76 100644 --- a/libraries/BLE/src/BLEDescriptor.cpp +++ b/libraries/BLE/src/BLEDescriptor.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -109,7 +108,7 @@ void BLEDescriptor::executeCreate(BLECharacteristic *pCharacteristic) { * @brief Get the BLE handle for this descriptor. * @return The handle for this descriptor. */ -uint16_t BLEDescriptor::getHandle() { +uint16_t BLEDescriptor::getHandle() const { return m_handle; } // getHandle @@ -117,14 +116,14 @@ uint16_t BLEDescriptor::getHandle() { * @brief Get the length of the value of this descriptor. * @return The length (in bytes) of the value of this descriptor. */ -size_t BLEDescriptor::getLength() { +size_t BLEDescriptor::getLength() const { return m_value.attr_len; } // getLength /** * @brief Get the UUID of the descriptor. */ -BLEUUID BLEDescriptor::getUUID() { +BLEUUID BLEDescriptor::getUUID() const { return m_bleUUID; } // getUUID @@ -132,7 +131,7 @@ BLEUUID BLEDescriptor::getUUID() { * @brief Get the value of this descriptor. * @return A pointer to the value of this descriptor. */ -uint8_t *BLEDescriptor::getValue() { +uint8_t *BLEDescriptor::getValue() const { return m_value.attr_value; } // getValue @@ -140,7 +139,7 @@ uint8_t *BLEDescriptor::getValue() { * @brief Get the characteristic this descriptor belongs to. * @return A pointer to the characteristic this descriptor belongs to. */ -BLECharacteristic *BLEDescriptor::getCharacteristic() { +BLECharacteristic *BLEDescriptor::getCharacteristic() const { return m_pCharacteristic; } // getCharacteristic @@ -181,7 +180,7 @@ void BLEDescriptor::setHandle(uint16_t handle) { * @param [in] data The data to set for the descriptor. * @param [in] length The length of the data in bytes. */ -void BLEDescriptor::setValue(uint8_t *data, size_t length) { +void BLEDescriptor::setValue(const uint8_t *data, size_t length) { if (length > m_value.attr_max_len) { log_e("Size %d too large, must be no bigger than %d", length, m_value.attr_max_len); return; @@ -203,11 +202,11 @@ void BLEDescriptor::setValue(uint8_t *data, size_t length) { * @brief Set the value of the descriptor. * @param [in] value The value of the descriptor in string form. */ -void BLEDescriptor::setValue(String value) { - setValue((uint8_t *)value.c_str(), value.length()); +void BLEDescriptor::setValue(const String &value) { + setValue(reinterpret_cast(value.c_str()), value.length()); } // setValue -void BLEDescriptor::setAccessPermissions(uint8_t perm) { +void BLEDescriptor::setAccessPermissions(uint16_t perm) { m_permissions = perm; } @@ -215,14 +214,14 @@ void BLEDescriptor::setAccessPermissions(uint8_t perm) { * @brief Return a string representation of the descriptor. * @return A string representation of the descriptor. */ -String BLEDescriptor::toString() { +String BLEDescriptor::toString() const { char hex[5]; snprintf(hex, sizeof(hex), "%04x", m_handle); String res = "UUID: " + m_bleUUID.toString() + ", handle: 0x" + hex; return res; } // toString -BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} +BLEDescriptorCallbacks::~BLEDescriptorCallbacks() = default; /** * @brief Callback function to support a read request. @@ -406,4 +405,4 @@ int BLEDescriptor::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_han #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDescriptor.h b/libraries/BLE/src/BLEDescriptor.h index d0d34b24388..be107796cb3 100644 --- a/libraries/BLE/src/BLEDescriptor.h +++ b/libraries/BLE/src/BLEDescriptor.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ #define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -42,6 +42,9 @@ #include #include "BLEConnInfo.h" +// Bluedroid compatibility +// NimBLE does not support signed reads and writes + #define ESP_GATT_PERM_READ BLE_ATT_F_READ #define ESP_GATT_PERM_WRITE BLE_ATT_F_WRITE #define ESP_GATT_PERM_READ_ENCRYPTED BLE_ATT_F_READ_ENC @@ -86,18 +89,18 @@ class BLEDescriptor { BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); virtual ~BLEDescriptor(); - uint16_t getHandle(); // Get the handle of the descriptor. - size_t getLength(); // Get the length of the value of the descriptor. - BLEUUID getUUID(); // Get the UUID of the descriptor. - uint8_t *getValue(); // Get a pointer to the value of the descriptor. - BLECharacteristic *getCharacteristic(); // Get the characteristic that this descriptor belongs to. + uint16_t getHandle() const; // Get the handle of the descriptor. + size_t getLength() const; // Get the length of the value of the descriptor. + BLEUUID getUUID() const; // Get the UUID of the descriptor. + uint8_t *getValue() const; // Get a pointer to the value of the descriptor. + BLECharacteristic *getCharacteristic() const; // Get the characteristic that this descriptor belongs to. - void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. + void setAccessPermissions(uint16_t perm); // Set the permissions of the descriptor. void setCallbacks(BLEDescriptorCallbacks *pCallbacks); // Set callbacks to be invoked for the descriptor. - void setValue(uint8_t *data, size_t size); // Set the value of the descriptor as a pointer to data. - void setValue(String value); // Set the value of the descriptor as a data buffer. + void setValue(const uint8_t *data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(const String &value); // Set the value of the descriptor as a data buffer. - String toString(); // Convert the descriptor to a string representation. + String toString() const; // Convert the descriptor to a string representation. /*************************************************************************** * Bluedroid public declarations * @@ -140,7 +143,7 @@ class BLEDescriptor { #if defined(CONFIG_BLUEDROID_ENABLED) FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - uint8_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + uint16_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; #endif /*************************************************************************** @@ -179,5 +182,6 @@ class BLEDescriptorCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ diff --git a/libraries/BLE/src/BLEDescriptorMap.cpp b/libraries/BLE/src/BLEDescriptorMap.cpp index e0c8b3a7cc3..39a54dc27bf 100644 --- a/libraries/BLE/src/BLEDescriptorMap.cpp +++ b/libraries/BLE/src/BLEDescriptorMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -52,7 +51,7 @@ * @param [in] UUID The UUID to look up the descriptor. * @return The descriptor. If not present, then nullptr is returned. */ -BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) { +BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) const { return getByUUID(BLEUUID(uuid)); } @@ -61,7 +60,7 @@ BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) { * @param [in] UUID The UUID to look up the descriptor. * @return The descriptor. If not present, then nullptr is returned. */ -BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) { +BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) const { for (auto &myPair : m_uuidMap) { if (myPair.first->getUUID().equals(uuid)) { return myPair.first; @@ -76,7 +75,7 @@ BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) { * @param [in] handle The handle to look up the descriptor. * @return The descriptor. */ -BLEDescriptor *BLEDescriptorMap::getByHandle(uint16_t handle) { +BLEDescriptor *BLEDescriptorMap::getByHandle(uint16_t handle) const { return m_handleMap.at(handle); } // getByHandle @@ -114,7 +113,7 @@ void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor *pDescriptor) * @brief Get the number of registered descriptors. * @return The number of registered descriptors. */ -int BLEDescriptorMap::getRegisteredDescriptorCount() { +int BLEDescriptorMap::getRegisteredDescriptorCount() const { return m_uuidMap.size(); } @@ -132,7 +131,7 @@ void BLEDescriptorMap::removeDescriptor(BLEDescriptor *pDescriptor) { * @brief Return a string representation of the descriptor map. * @return A string representation of the descriptor map. */ -String BLEDescriptorMap::toString() { +String BLEDescriptorMap::toString() const { String res; char hex[5]; int count = 0; @@ -213,4 +212,4 @@ void BLEDescriptorMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDevice.cpp b/libraries/BLE/src/BLEDevice.cpp index 014806cc32b..c184fada279 100644 --- a/libraries/BLE/src/BLEDevice.cpp +++ b/libraries/BLE/src/BLEDevice.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -24,7 +23,10 @@ #include #include #include + +#if SOC_BLE_SUPPORTED #include +#endif #include #include @@ -35,6 +37,8 @@ #include "BLEClient.h" #include "BLEUtils.h" #include "GeneralUtils.h" +#include "BLESecurity.h" +#include "base64.h" #if defined(ARDUINO_ARCH_ESP32) #include "esp32-hal-bt.h" @@ -74,6 +78,10 @@ #include "services/gatt/ble_svc_gatt.h" #endif +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#include "esp32-hal-hosted.h" +#endif + /*************************************************************************** * Common properties * ***************************************************************************/ @@ -97,7 +105,6 @@ gap_event_handler BLEDevice::m_customGapHandler = nullptr; ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; #endif @@ -236,6 +243,8 @@ String BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID ch */ void BLEDevice::init(String deviceName) { if (!initialized) { + log_i("Initializing BLE stack: %s", getBLEStackString().c_str()); + esp_err_t errRc = ESP_OK; #if defined(CONFIG_BLUEDROID_ENABLED) #if defined(ARDUINO_ARCH_ESP32) @@ -339,6 +348,16 @@ void BLEDevice::init(String deviceName) { #endif // CONFIG_BLE_SMP_ENABLE #endif // CONFIG_BLUEDROID_ENABLED +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + // Initialize esp-hosted transport for BLE HCI when explicitly enabled + if (!hostedInitBLE()) { + log_e("Failed to initialize ESP-Hosted for BLE"); + return; + } + + // Hosted HCI driver will be initialized automatically by NimBLE transport layer +#endif + #if defined(CONFIG_NIMBLE_ENABLED) errRc = nimble_port_init(); if (errRc != ESP_OK) { @@ -410,10 +429,14 @@ void BLEDevice::init(String deviceName) { */ void BLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { log_v(">> setPower: %d (type: %d)", powerLevel, powerType); +#if defined(SOC_BLE_SUPPORTED) esp_err_t errRc = ::esp_ble_tx_power_set(powerType, powerLevel); if (errRc != ESP_OK) { log_e("esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); }; +#else + log_w("setPower not supported with hosted HCI - power controlled by co-processor"); +#endif log_v("<< setPower"); } // setPower @@ -436,17 +459,22 @@ void BLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powe */ int BLEDevice::getPower(esp_ble_power_type_t powerType) { +#if SOC_BLE_SUPPORTED switch (esp_ble_tx_power_get(powerType)) { case ESP_PWR_LVL_N12: return -12; case ESP_PWR_LVL_N9: return -9; case ESP_PWR_LVL_N6: return -6; - case ESP_PWR_LVL_N3: return -6; + case ESP_PWR_LVL_N3: return -3; case ESP_PWR_LVL_N0: return 0; case ESP_PWR_LVL_P3: return 3; case ESP_PWR_LVL_P6: return 6; case ESP_PWR_LVL_P9: return 9; default: return -128; } +#else + log_w("getPower not supported with hosted HCI - power controlled by co-processor"); + return 0; // Return default power level +#endif } // getPower /** @@ -581,6 +609,181 @@ bool BLEDevice::getInitialized() { return initialized; } +/* + * @brief Get a peer device's Identity Resolving Key (IRK). + * @param [in] peerAddress The address of the bonded peer device. + * @param [out] irk Buffer to store the 16-byte IRK. + * @return True if successful, false otherwise. + * @note IRK is only available after bonding has occurred. + */ +bool BLEDevice::getPeerIRK(BLEAddress peerAddress, uint8_t *irk) { + log_v(">> BLEDevice::getPeerIRK()"); + + if (irk == nullptr) { + log_e("IRK buffer is null"); + return false; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) + // Get the list of bonded devices + int dev_num = esp_ble_get_bond_device_num(); + if (dev_num == 0) { + log_e("No bonded devices found"); + return false; + } + + esp_ble_bond_dev_t *bond_dev = (esp_ble_bond_dev_t *)malloc(sizeof(esp_ble_bond_dev_t) * dev_num); + if (bond_dev == nullptr) { + log_e("Failed to allocate memory for bond device list"); + return false; + } + + esp_err_t ret = esp_ble_get_bond_device_list(&dev_num, bond_dev); + if (ret != ESP_OK) { + log_e("Failed to get bond device list: %d", ret); + free(bond_dev); + return false; + } + + // Find the bonded device that matches the peer address + bool found = false; + + for (int i = 0; i < dev_num; i++) { + BLEAddress bondAddr(bond_dev[i].bd_addr); + if (bondAddr.equals(peerAddress)) { + // Check if the PID key (which contains the IRK) is present + if (bond_dev[i].bond_key.key_mask & ESP_LE_KEY_PID) { + memcpy(irk, bond_dev[i].bond_key.pid_key.irk, 16); + found = true; + log_d("IRK found for peer: %s", peerAddress.toString().c_str()); + break; + } else { + log_w("PID key not present for peer: %s", peerAddress.toString().c_str()); + } + } + } + + free(bond_dev); + + if (!found) { + log_e("IRK not found for peer"); + return false; + } + + log_v("<< BLEDevice::getPeerIRK()"); + return true; +#endif // CONFIG_BLUEDROID_ENABLED + +#if defined(CONFIG_NIMBLE_ENABLED) + // Prepare the key structure to search for the peer's security information + struct ble_store_key_sec key_sec; + memset(&key_sec, 0, sizeof(key_sec)); + + // Convert BLEAddress to ble_addr_t + // NOTE: BLEAddress stores bytes in INVERSE order for NimBLE, + // but ble_addr_t.val expects them in normal order, so we reverse them + ble_addr_t addr; + uint8_t *peer_addr = peerAddress.getNative(); + for (int i = 0; i < 6; i++) { + addr.val[i] = peer_addr[5 - i]; + } + + // Try public address first, then random if that fails + addr.type = BLE_ADDR_PUBLIC; + memcpy(&key_sec.peer_addr, &addr, sizeof(ble_addr_t)); + + // Read the peer's security information from the store + struct ble_store_value_sec value_sec; + int rc = ble_store_read_peer_sec(&key_sec, &value_sec); + + // If public address failed, try random address type + if (rc != 0) { + addr.type = BLE_ADDR_RANDOM; + memcpy(&key_sec.peer_addr, &addr, sizeof(ble_addr_t)); + rc = ble_store_read_peer_sec(&key_sec, &value_sec); + + if (rc != 0) { + log_e("IRK not found for peer: %s", peerAddress.toString().c_str()); + return false; + } + } + + // Check if the IRK is present + if (!value_sec.irk_present) { + log_e("IRK not present for peer"); + return false; + } + + // Copy the IRK to the output buffer + memcpy(irk, value_sec.irk, 16); + + log_d("IRK found for peer: %s (type=%d)", peerAddress.toString().c_str(), addr.type); + log_v("<< BLEDevice::getPeerIRK()"); + return true; +#endif // CONFIG_NIMBLE_ENABLED +} + +/* + * @brief Get a peer device's IRK as a comma-separated hex string. + * @param [in] peerAddress The address of the bonded peer device. + * @return String in format "0xXX,0xXX,..." or empty string on failure. + */ +String BLEDevice::getPeerIRKString(BLEAddress peerAddress) { + uint8_t irk[16]; + if (!getPeerIRK(peerAddress, irk)) { + return String(); + } + + String result = ""; + for (int i = 0; i < 16; i++) { + result += "0x"; + if (irk[i] < 0x10) { + result += "0"; + } + result += String(irk[i], HEX); + if (i < 15) { + result += ","; + } + } + return result; +} + +/* + * @brief Get a peer device's IRK as a Base64 encoded string. + * @param [in] peerAddress The address of the bonded peer device. + * @return Base64 encoded string or empty string on failure. + */ +String BLEDevice::getPeerIRKBase64(BLEAddress peerAddress) { + uint8_t irk[16]; + if (!getPeerIRK(peerAddress, irk)) { + return String(); + } + + return base64::encode(irk, 16); +} + +/* + * @brief Get a peer device's IRK in reverse hex format. + * @param [in] peerAddress The address of the bonded peer device. + * @return String in reverse hex format (uppercase) or empty string on failure. + */ +String BLEDevice::getPeerIRKReverse(BLEAddress peerAddress) { + uint8_t irk[16]; + if (!getPeerIRK(peerAddress, irk)) { + return String(); + } + + String result = ""; + for (int i = 15; i >= 0; i--) { + if (irk[i] < 0x10) { + result += "0"; + } + result += String(irk[i], HEX); + } + result.toUpperCase(); + return result; +} + BLEAdvertising *BLEDevice::getAdvertising() { if (m_bleAdvertising == nullptr) { m_bleAdvertising = new BLEAdvertising(); @@ -664,13 +867,47 @@ void BLEDevice::removePeerDevice(uint16_t conn_id, bool _client) { /** * @brief de-Initialize the %BLE environment. - * @param release_memory release the internal BT stack memory + * @param release_memory release the internal BT stack memory (prevents reinitialization) */ void BLEDevice::deinit(bool release_memory) { if (!initialized) { return; } + // Stop advertising and scanning first + if (m_bleAdvertising != nullptr) { + m_bleAdvertising->stop(); + } + + if (m_pScan != nullptr) { + m_pScan->stop(); + } + + // Delete all BLE objects + if (m_bleAdvertising != nullptr) { + delete m_bleAdvertising; + m_bleAdvertising = nullptr; + } + + if (m_pScan != nullptr) { + delete m_pScan; + m_pScan = nullptr; + } + + if (m_pServer != nullptr) { + delete m_pServer; + m_pServer = nullptr; + } + + if (m_pClient != nullptr) { + delete m_pClient; + m_pClient = nullptr; + } + + // Clear the connected clients map + m_connectedClientsMap.clear(); + + // Always deinit the BLE stack #ifdef CONFIG_BLUEDROID_ENABLED esp_bluedroid_disable(); esp_bluedroid_deinit(); @@ -681,20 +918,29 @@ void BLEDevice::deinit(bool release_memory) { nimble_port_deinit(); #endif +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + hostedDeinitBLE(); +#endif + +#if CONFIG_BT_CONTROLLER_ENABLED esp_bt_controller_disable(); esp_bt_controller_deinit(); +#endif -#ifdef ARDUINO_ARCH_ESP32 + // Only release memory if requested (this prevents reinitialization) if (release_memory) { +#ifdef ARDUINO_ARCH_ESP32 // Require tests because we released classic BT memory and this can cause crash (most likely not, esp-idf takes care of it) +#if CONFIG_BT_CONTROLLER_ENABLED esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); - } else { -#ifdef CONFIG_NIMBLE_ENABLED - m_synced = false; #endif - initialized = false; +#endif } + +#ifdef CONFIG_NIMBLE_ENABLED + m_synced = false; #endif + initialized = false; } void BLEDevice::setCustomGapHandler(gap_event_handler handler) { @@ -709,6 +955,33 @@ void BLEDevice::setCustomGapHandler(gap_event_handler handler) { #endif } +BLEStack BLEDevice::getBLEStack() { +#if defined(CONFIG_BLUEDROID_ENABLED) + return BLEStack::BLUEDROID; +#elif defined(CONFIG_NIMBLE_ENABLED) + return BLEStack::NIMBLE; +#else + return BLEStack::UNKNOWN; +#endif +} + +String BLEDevice::getBLEStackString() { + switch (getBLEStack()) { + case BLEStack::BLUEDROID: return "Bluedroid"; + case BLEStack::NIMBLE: return "NimBLE"; + case BLEStack::UNKNOWN: + default: return "Unknown"; + } +} + +bool BLEDevice::isHostedBLE() { +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + return true; +#else + return false; +#endif +} + /*************************************************************************** * Bluedroid functions * ***************************************************************************/ @@ -730,11 +1003,9 @@ void BLEDevice::gattServerEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t switch (event) { case ESP_GATTS_CONNECT_EVT: { -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(param->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT @@ -771,11 +1042,11 @@ void BLEDevice::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t switch (event) { case ESP_GATTC_CONNECT_EVT: { -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + // Set encryption on connect for BlueDroid when security is enabled + // This ensures security is established before any secure operations + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(param->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT @@ -807,26 +1078,49 @@ void BLEDevice::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_par case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ log_i("ESP_GAP_BLE_OOB_REQ_EVT"); break; case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ log_i("ESP_GAP_BLE_LOCAL_IR_EVT"); break; case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ log_i("ESP_GAP_BLE_LOCAL_ER_EVT"); break; - case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ log_i("ESP_GAP_BLE_NC_REQ_EVT"); + case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ + { + log_i("ESP_GAP_BLE_NC_REQ_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } else { + log_e("onConfirmPIN not implemented. Rejecting connection"); + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, false); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + { log_i("ESP_GAP_BLE_PASSKEY_REQ_EVT: "); // esp_log_buffer_hex(m_remote_bda, sizeof(m_remote_bda)); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { - esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); + uint32_t passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } } + + if (BLESecurity::m_staticPasskey && passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", passkey); + } + + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, passkey); #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; /* * TODO should we add white/black list comparison? */ case ESP_GAP_BLE_SEC_REQ_EVT: + { /* send the positive(true) security response to the peer device to accept the security request. If not accept the security request, should sent the security response with negative(false) accept value*/ log_i("ESP_GAP_BLE_SEC_REQ_EVT"); @@ -834,37 +1128,58 @@ void BLEDevice::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_par if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); } else { + log_w("onSecurityRequest not implemented. Accepting security request"); esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; /* * */ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + { //display the passkey number to the user to input it in the peer device within 30 seconds log_i("ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - log_i("passKey = %d", param->ble_security.key_notif.passkey); + uint32_t passkey = param->ble_security.key_notif.passkey; + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); + } + + if (BLESecurity::m_staticPasskey && passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", passkey); + } + if (BLEDevice::m_securityCallbacks != nullptr) { - BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + BLEDevice::m_securityCallbacks->onPassKeyNotify(passkey); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; case ESP_GAP_BLE_KEY_EVT: + { //shows the ble key type info share with peer device to the user. log_d("ESP_GAP_BLE_KEY_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig log_i("key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: log_i("ESP_GAP_BLE_AUTH_CMPL_EVT"); + } break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + { + log_i("ESP_GAP_BLE_AUTH_CMPL_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig + // Signal that authentication has completed + // This unblocks any GATT operations waiting for pairing when bonding is enabled + BLESecurity::signalAuthenticationComplete(); + if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; default: { break; @@ -896,14 +1211,6 @@ void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { m_customGattsHandler = handler; } -/* - * @brief Set encryption level that will be negotiated with peer device durng connection - * @param [in] level Requested encryption level - */ -void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { - BLEDevice::m_securityLevel = level; -} - #endif /*************************************************************************** @@ -912,6 +1219,23 @@ void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { #if defined(CONFIG_NIMBLE_ENABLED) +/** + * @brief Set the SDIO pins for connection to external ESP MCU when using ESP-Hosted with NimBLE + * @param [in] clk The clock pin + * @param [in] cmd The command pin + * @param [in] d0 The data pin 0 + * @param [in] d1 The data pin 1 + * @param [in] d2 The data pin 2 + * @param [in] d3 The data pin 3 + * @param [in] rst The reset pin + * @return True if the pins were set successfully. + */ +#if CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +bool BLEDevice::setPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { + return hostedSetPins(clk, cmd, d0, d1, d2, d3, rst); +} +#endif + /** * @brief Checks if a peer device is whitelisted. * @param [in] address The address to check for in the whitelist. @@ -1062,4 +1386,4 @@ int BLEDeviceCallbacks::onStoreStatus(struct ble_store_status_event *event, void #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDevice.h b/libraries/BLE/src/BLEDevice.h index 66cfa2b371a..5d4a16295a8 100644 --- a/libraries/BLE/src/BLEDevice.h +++ b/libraries/BLE/src/BLEDevice.h @@ -11,10 +11,10 @@ #ifndef MAIN_BLEDevice_H_ #define MAIN_BLEDevice_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -22,8 +22,19 @@ ***************************************************************************/ #include -#include + +#if defined(SOC_BLE_SUPPORTED) #include +#else +// For ESP32-P4 and other chips without native BLE support +// Define minimal types needed for interface compatibility +typedef int esp_power_level_t; +typedef int esp_ble_power_type_t; +#define ESP_BLE_PWR_TYPE_DEFAULT 0 +#define ESP_PWR_LVL_N12 0 +#endif + +#include "WString.h" #include "BLEServer.h" #include "BLEClient.h" #include "BLEUtils.h" @@ -32,6 +43,18 @@ #include "BLESecurity.h" #include "BLEAddress.h" #include "BLEUtils.h" +#include "BLEUUID.h" +#include "BLEAdvertisedDevice.h" + +/*************************************************************************** + * Common definitions * + ***************************************************************************/ + +enum class BLEStack { + BLUEDROID, + NIMBLE, + UNKNOWN +}; /*************************************************************************** * Bluedroid includes * @@ -51,6 +74,9 @@ #include #define ESP_GATT_IF_NONE BLE_HS_CONN_HANDLE_NONE +// Hosted HCI transport implementation is provided in BLEHostedHCI.cpp +// and is automatically linked when building for ESP32-P4 + // NimBLE configuration compatibility macros #if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE) #define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR @@ -141,7 +167,6 @@ class BLEDevice { ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - static esp_ble_sec_act_t m_securityLevel; static gattc_event_handler m_customGattcHandler; static gatts_event_handler m_customGattsHandler; #endif @@ -167,6 +192,10 @@ class BLEDevice { static esp_err_t setMTU(uint16_t mtu); static uint16_t getMTU(); static bool getInitialized(); + static bool getPeerIRK(BLEAddress peerAddress, uint8_t *irk); + static String getPeerIRKString(BLEAddress peerAddress); + static String getPeerIRKBase64(BLEAddress peerAddress); + static String getPeerIRKReverse(BLEAddress peerAddress); static BLEAdvertising *getAdvertising(); static void startAdvertising(); static void stopAdvertising(); @@ -179,13 +208,15 @@ class BLEDevice { static BLEClient *getClientByGattIf(uint16_t conn_id); static void setCustomGapHandler(gap_event_handler handler); static void deinit(bool release_memory = false); + static BLEStack getBLEStack(); + static String getBLEStackString(); + static bool isHostedBLE(); /*************************************************************************** * Bluedroid public declarations * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - static void setEncryptionLevel(esp_ble_sec_act_t level); static void setCustomGattcHandler(gattc_event_handler handler); static void setCustomGattsHandler(gatts_event_handler handler); #endif @@ -203,6 +234,10 @@ class BLEDevice { static bool setOwnAddr(uint8_t *addr); static void setDeviceCallbacks(BLEDeviceCallbacks *cb); static bool onWhiteList(BLEAddress &address); +#if CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE + // Set SDIO pins for connection to external ESP MCU + static bool setPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst); +#endif #endif private: @@ -262,5 +297,6 @@ class BLEDeviceCallbacks { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* MAIN_BLEDevice_H_ */ diff --git a/libraries/BLE/src/BLEEddystoneTLM.cpp b/libraries/BLE/src/BLEEddystoneTLM.cpp index ca0fb41b1a2..8b642c8136f 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.cpp +++ b/libraries/BLE/src/BLEEddystoneTLM.cpp @@ -14,10 +14,10 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) + #include #include #include "esp32-hal-log.h" @@ -181,4 +181,4 @@ void BLEEddystoneTLM::setTime(uint32_t tmil) { } // setTime #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEEddystoneTLM.h b/libraries/BLE/src/BLEEddystoneTLM.h index 17915a670da..2aa30c2d21b 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.h +++ b/libraries/BLE/src/BLEEddystoneTLM.h @@ -11,10 +11,10 @@ #ifndef _BLEEddystoneTLM_H_ #define _BLEEddystoneTLM_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLEUUID.h" @@ -65,5 +65,6 @@ class BLEEddystoneTLM { }; // BLEEddystoneTLM #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEEddystoneTLM_H_ */ diff --git a/libraries/BLE/src/BLEEddystoneURL.cpp b/libraries/BLE/src/BLEEddystoneURL.cpp index 495671ca49b..00ca18b71d3 100644 --- a/libraries/BLE/src/BLEEddystoneURL.cpp +++ b/libraries/BLE/src/BLEEddystoneURL.cpp @@ -13,10 +13,10 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) + #include #include "esp32-hal-log.h" #include "BLEEddystoneURL.h" @@ -174,34 +174,22 @@ void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { } // setUUID void BLEEddystoneURL::setPower(esp_power_level_t advertisedTxPower) { - int tx_power; + int tx_power = 0; +#if SOC_BLE_SUPPORTED switch (advertisedTxPower) { - case ESP_PWR_LVL_N12: // 12dbm - tx_power = -12; - break; - case ESP_PWR_LVL_N9: // -9dbm - tx_power = -9; - break; - case ESP_PWR_LVL_N6: // -6dbm - tx_power = -6; - break; - case ESP_PWR_LVL_N3: // -3dbm - tx_power = -3; - break; - case ESP_PWR_LVL_N0: // 0dbm - tx_power = 0; - break; - case ESP_PWR_LVL_P3: // +3dbm - tx_power = +3; - break; - case ESP_PWR_LVL_P6: // +6dbm - tx_power = +6; - break; - case ESP_PWR_LVL_P9: // +9dbm - tx_power = +9; - break; - default: tx_power = 0; + case ESP_PWR_LVL_N12: tx_power = -12; break; + case ESP_PWR_LVL_N9: tx_power = -9; break; + case ESP_PWR_LVL_N6: tx_power = -6; break; + case ESP_PWR_LVL_N3: tx_power = -3; break; + case ESP_PWR_LVL_N0: tx_power = 0; break; + case ESP_PWR_LVL_P3: tx_power = +3; break; + case ESP_PWR_LVL_P6: tx_power = +6; break; + case ESP_PWR_LVL_P9: tx_power = +9; break; + default: tx_power = 0; break; } +#else + log_w("setPower not supported with hosted HCI - power controlled by co-processor"); +#endif m_eddystoneData.advertisedTxPower = int8_t((tx_power - -100) / 2); } // setPower @@ -300,4 +288,4 @@ void BLEEddystoneURL::_initHeadder() { } #endif -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEEddystoneURL.h b/libraries/BLE/src/BLEEddystoneURL.h index 9ed89a23694..f22035093ea 100644 --- a/libraries/BLE/src/BLEEddystoneURL.h +++ b/libraries/BLE/src/BLEEddystoneURL.h @@ -14,15 +14,17 @@ #ifndef _BLEEddystoneURL_H_ #define _BLEEddystoneURL_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLEUUID.h" #include -#include "esp_bt.h" +#if SOC_BLE_SUPPORTED +#include +#endif #define EDDYSTONE_URL_FRAME_TYPE 0x10 @@ -65,5 +67,6 @@ class BLEEddystoneURL { }; // BLEEddystoneURL #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEEddystoneURL_H_ */ diff --git a/libraries/BLE/src/BLEExceptions.cpp b/libraries/BLE/src/BLEExceptions.cpp index b88ea337493..f566f7851b0 100644 --- a/libraries/BLE/src/BLEExceptions.cpp +++ b/libraries/BLE/src/BLEExceptions.cpp @@ -6,8 +6,9 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) //#include "BLEExceptions.h" -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEExceptions.h b/libraries/BLE/src/BLEExceptions.h index 15b4ef93d84..91cb184cb27 100644 --- a/libraries/BLE/src/BLEExceptions.h +++ b/libraries/BLE/src/BLEExceptions.h @@ -7,10 +7,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ #define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if CONFIG_CXX_EXCEPTIONS != 1 #error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." @@ -30,5 +30,6 @@ class BLEUuidNotFoundException : public std::exception { } }; -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ diff --git a/libraries/BLE/src/BLEHIDDevice.cpp b/libraries/BLE/src/BLEHIDDevice.cpp index a255879e2d8..20f9e800bb9 100644 --- a/libraries/BLE/src/BLEHIDDevice.cpp +++ b/libraries/BLE/src/BLEHIDDevice.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -282,4 +281,4 @@ BLEService *BLEHIDDevice::batteryService() { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEHIDDevice.h b/libraries/BLE/src/BLEHIDDevice.h index 9dde9452c12..a34807536d7 100644 --- a/libraries/BLE/src/BLEHIDDevice.h +++ b/libraries/BLE/src/BLEHIDDevice.h @@ -13,9 +13,8 @@ #define _BLEHIDDEVICE_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLECharacteristic.h" @@ -80,5 +79,6 @@ class BLEHIDDevice { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEHIDDEVICE_H_ */ diff --git a/libraries/BLE/src/BLERemoteCharacteristic.cpp b/libraries/BLE/src/BLERemoteCharacteristic.cpp index aec9500d6f3..70a91288ab2 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.cpp +++ b/libraries/BLE/src/BLERemoteCharacteristic.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -113,6 +112,11 @@ bool BLERemoteCharacteristic::canWriteNoResponse() { * @brief Retrieve the map of descriptors keyed by UUID. */ std::map *BLERemoteCharacteristic::getDescriptors() { + // Retrieve descriptors if not already done (lazy loading) + if (!m_descriptorsRetrieved) { + log_d("Descriptors not yet retrieved, retrieving now..."); + retrieveDescriptors(); + } return &m_descriptorMap; } // getDescriptors @@ -133,6 +137,11 @@ uint16_t BLERemoteCharacteristic::getHandle() { */ BLERemoteDescriptor *BLERemoteCharacteristic::getDescriptor(BLEUUID uuid) { log_v(">> getDescriptor: uuid: %s", uuid.toString().c_str()); + // Retrieve descriptors if not already done (lazy loading) + if (!m_descriptorsRetrieved) { + log_d("Descriptors not yet retrieved, retrieving now..."); + retrieveDescriptors(); + } std::string v = uuid.toString().c_str(); for (auto &myPair : m_descriptorMap) { if (myPair.first == v) { @@ -288,6 +297,7 @@ void BLERemoteCharacteristic::removeDescriptors() { delete myPair.second; } m_descriptorMap.clear(); + m_descriptorsRetrieved = false; // Allow descriptors to be retrieved again } // removeCharacteristics /** @@ -367,6 +377,7 @@ BLERemoteCharacteristic::BLERemoteCharacteristic(uint16_t handle, BLEUUID uuid, m_notifyCallback = nullptr; m_rawData = nullptr; m_auth = ESP_GATT_AUTH_REQ_NONE; + m_descriptorsRetrieved = false; retrieveDescriptors(); // Get the descriptors for this characteristic log_v("<< BLERemoteCharacteristic"); @@ -550,6 +561,7 @@ void BLERemoteCharacteristic::retrieveDescriptors() { offset++; } // while true //m_haveCharacteristics = true; // Remember that we have received the characteristics. + m_descriptorsRetrieved = true; log_v("<< retrieveDescriptors(): Found %d descriptors.", offset); } // getDescriptors @@ -566,6 +578,10 @@ String BLERemoteCharacteristic::readValue() { return String(); } + // Wait for authentication to complete if bonding is enabled + // This prevents the read request from being made while pairing is in progress + BLESecurity::waitForAuthenticationComplete(); + m_semaphoreReadCharEvt.take("readValue"); // Ask the BLE subsystem to retrieve the value for the remote hosted characteristic. @@ -608,6 +624,10 @@ bool BLERemoteCharacteristic::writeValue(uint8_t *data, size_t length, bool resp return false; } + // Wait for authentication to complete if bonding is enabled + // This prevents the write request from being made while pairing is in progress + BLESecurity::waitForAuthenticationComplete(); + m_semaphoreWriteCharEvt.take("writeValue"); // Invoke the ESP-IDF API to perform the write. esp_err_t errRc = ::esp_ble_gattc_write_char( @@ -656,14 +676,15 @@ BLERemoteCharacteristic::BLERemoteCharacteristic(BLERemoteService *pRemoteServic m_handle = chr->val_handle; m_defHandle = chr->def_handle; - m_endHandle = 0; m_charProp = chr->properties; m_pRemoteService = pRemoteService; m_notifyCallback = nullptr; m_rawData = nullptr; m_auth = 0; + m_descriptorsRetrieved = false; - retrieveDescriptors(); // Get the descriptors for this characteristic + // Don't retrieve descriptors in constructor for NimBLE to avoid deadlock + // Descriptors will be retrieved on-demand when needed (e.g., for notifications) log_v("<< BLERemoteCharacteristic(): %s", m_uuid.toString().c_str()); } // BLERemoteCharacteristic @@ -774,6 +795,7 @@ bool BLERemoteCharacteristic::retrieveDescriptors(const BLEUUID *uuid_filter) { // If this is the last handle then there are no descriptors if (m_handle == getRemoteService()->getEndHandle()) { + m_descriptorsRetrieved = true; log_d("<< retrieveDescriptors(): No descriptors found"); return true; } @@ -782,7 +804,9 @@ bool BLERemoteCharacteristic::retrieveDescriptors(const BLEUUID *uuid_filter) { desc_filter_t filter = {uuid_filter, &taskData}; int rc = 0; - rc = ble_gattc_disc_all_dscs(getRemoteService()->getClient()->getConnId(), m_handle, m_endHandle, BLERemoteCharacteristic::descriptorDiscCB, &filter); + rc = ble_gattc_disc_all_dscs( + getRemoteService()->getClient()->getConnId(), m_handle, getRemoteService()->getEndHandle(), BLERemoteCharacteristic::descriptorDiscCB, &filter + ); if (rc != 0) { log_e("ble_gattc_disc_all_dscs: rc=%d %s", rc, BLEUtils::returnCodeToString(rc)); @@ -799,6 +823,7 @@ bool BLERemoteCharacteristic::retrieveDescriptors(const BLEUUID *uuid_filter) { return false; } + m_descriptorsRetrieved = true; log_d("<< retrieveDescriptors(): Found %d descriptors.", m_descriptorMap.size() - prevDscCount); return true; } // retrieveDescriptors @@ -846,7 +871,7 @@ String BLERemoteCharacteristic::readValue() { case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -928,10 +953,10 @@ bool BLERemoteCharacteristic::writeValue(uint8_t *data, size_t length, bool resp case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } - /* Else falls through. */ + /* Else falls through. */ default: goto exit; } } while (rc != 0 && retryCount--); @@ -959,6 +984,15 @@ bool BLERemoteCharacteristic::setNotify(uint16_t val, notify_callback notifyCall m_notifyCallback = notifyCallback; + // Retrieve descriptors if not already done (lazy loading) + if (!m_descriptorsRetrieved) { + log_d("Descriptors not yet retrieved, retrieving now..."); + if (!retrieveDescriptors()) { + log_e("<< setNotify(): Failed to retrieve descriptors"); + return false; + } + } + BLERemoteDescriptor *desc = getDescriptor(BLEUUID((uint16_t)0x2902)); if (desc == nullptr) { log_w("<< setNotify(): Callback set, CCCD not found"); @@ -999,4 +1033,4 @@ bool BLERemoteCharacteristic::unsubscribe(bool response) { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteCharacteristic.h b/libraries/BLE/src/BLERemoteCharacteristic.h index 81ad7b2f4f5..7b04a15c7cb 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.h +++ b/libraries/BLE/src/BLERemoteCharacteristic.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -45,6 +45,7 @@ #include #include +// Bluedroid Compatibility #define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN #define ESP_GATT_CHAR_PROP_BIT_READ BLE_GATT_CHR_PROP_READ #define ESP_GATT_CHAR_PROP_BIT_WRITE BLE_GATT_CHR_PROP_WRITE @@ -53,6 +54,12 @@ #define ESP_GATT_CHAR_PROP_BIT_NOTIFY BLE_GATT_CHR_PROP_NOTIFY #define ESP_GATT_CHAR_PROP_BIT_INDICATE BLE_GATT_CHR_PROP_INDICATE +#define ESP_GATT_AUTH_REQ_NONE 0 +#define ESP_GATT_AUTH_REQ_NO_MITM 1 +#define ESP_GATT_AUTH_REQ_MITM 2 +#define ESP_GATT_AUTH_REQ_SIGNED_NO_MITM 3 +#define ESP_GATT_AUTH_REQ_SIGNED_MITM 4 + #endif /*************************************************************************** @@ -145,6 +152,7 @@ class BLERemoteCharacteristic { // We maintain a map of descriptors owned by this characteristic keyed by a string representation of the UUID. std::map m_descriptorMap; + bool m_descriptorsRetrieved; // Flag to track if descriptor retrieval has been attempted /*************************************************************************** * NimBLE private properties * @@ -152,7 +160,6 @@ class BLERemoteCharacteristic { #if defined(CONFIG_NIMBLE_ENABLED) uint16_t m_defHandle; - uint16_t m_endHandle; #endif /*************************************************************************** @@ -186,5 +193,6 @@ class BLERemoteCharacteristic { }; // BLERemoteCharacteristic #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ diff --git a/libraries/BLE/src/BLERemoteDescriptor.cpp b/libraries/BLE/src/BLERemoteDescriptor.cpp index a142fe11880..e7cd3af38e6 100644 --- a/libraries/BLE/src/BLERemoteDescriptor.cpp +++ b/libraries/BLE/src/BLERemoteDescriptor.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -313,7 +312,7 @@ String BLERemoteDescriptor::readValue() { case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -459,7 +458,7 @@ bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -480,4 +479,4 @@ bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteDescriptor.h b/libraries/BLE/src/BLERemoteDescriptor.h index afe113df551..ea55fb81536 100644 --- a/libraries/BLE/src/BLERemoteDescriptor.h +++ b/libraries/BLE/src/BLERemoteDescriptor.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -103,5 +103,6 @@ class BLERemoteDescriptor { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ diff --git a/libraries/BLE/src/BLERemoteService.cpp b/libraries/BLE/src/BLERemoteService.cpp index 7baf6908d40..3cec81676be 100644 --- a/libraries/BLE/src/BLERemoteService.cpp +++ b/libraries/BLE/src/BLERemoteService.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -424,4 +423,4 @@ int BLERemoteService::characteristicDiscCB(uint16_t conn_handle, const struct bl #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteService.h b/libraries/BLE/src/BLERemoteService.h index c93d91f6852..977ccc85432 100644 --- a/libraries/BLE/src/BLERemoteService.h +++ b/libraries/BLE/src/BLERemoteService.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -128,5 +128,6 @@ class BLERemoteService { }; // BLERemoteService #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */ diff --git a/libraries/BLE/src/BLEScan.cpp b/libraries/BLE/src/BLEScan.cpp index 6d18bb2d751..54e60a2e423 100644 --- a/libraries/BLE/src/BLEScan.cpp +++ b/libraries/BLE/src/BLEScan.cpp @@ -13,9 +13,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -860,4 +859,4 @@ void BLEScan::onHostSync() { #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEScan.h b/libraries/BLE/src/BLEScan.h index 842b7144f06..327eb8c418d 100644 --- a/libraries/BLE/src/BLEScan.h +++ b/libraries/BLE/src/BLEScan.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ #define COMPONENTS_CPP_UTILS_BLESCAN_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -233,5 +233,6 @@ class BLEPeriodicScanCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ diff --git a/libraries/BLE/src/BLESecurity.cpp b/libraries/BLE/src/BLESecurity.cpp index 978026a3809..76757f52135 100644 --- a/libraries/BLE/src/BLESecurity.cpp +++ b/libraries/BLE/src/BLESecurity.cpp @@ -10,17 +10,19 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#include "Arduino.h" #include "BLESecurity.h" #include "BLEUtils.h" +#include "BLEDevice.h" +#include "GeneralUtils.h" #include "esp32-hal-log.h" /*************************************************************************** @@ -35,17 +37,48 @@ * Common properties * ***************************************************************************/ +// If true, the security will be enforced on connection even if no security is needed +// TODO: Make this configurable without breaking Bluedroid/NimBLE compatibility +bool BLESecurity::m_forceSecurity = true; + +bool BLESecurity::m_securityEnabled = false; +bool BLESecurity::m_securityStarted = false; +bool BLESecurity::m_passkeySet = false; +bool BLESecurity::m_staticPasskey = true; +bool BLESecurity::m_regenOnConnect = false; +bool BLESecurity::m_authenticationComplete = false; +uint8_t BLESecurity::m_iocap = ESP_IO_CAP_NONE; +uint8_t BLESecurity::m_authReq = 0; +uint8_t BLESecurity::m_initKey = 0; +uint8_t BLESecurity::m_respKey = 0; uint32_t BLESecurity::m_passkey = BLE_SM_DEFAULT_PASSKEY; /*************************************************************************** - * Common functions * + * Bluedroid properties * ***************************************************************************/ -BLESecurity::BLESecurity() {} +#if defined(CONFIG_BLUEDROID_ENABLED) +uint8_t BLESecurity::m_keySize = 16; +esp_ble_sec_act_t BLESecurity::m_securityLevel; +FreeRTOS::Semaphore *BLESecurity::m_authCompleteSemaphore = nullptr; +#endif + +/*************************************************************************** + * Common functions * + ***************************************************************************/ -BLESecurity::~BLESecurity() {} +// This function initializes the BLESecurity class. +BLESecurity::BLESecurity() { + log_d("BLESecurity: Initializing"); + setKeySize(); + setInitEncryptionKey(); + setRespEncryptionKey(); + setCapability(ESP_IO_CAP_NONE); +} +// This function sets the authentication mode for the BLE security. void BLESecurity::setAuthenticationMode(uint8_t auth_req) { + log_d("setAuthenticationMode: auth_req=%d", auth_req); m_authReq = auth_req; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); @@ -56,7 +89,9 @@ void BLESecurity::setAuthenticationMode(uint8_t auth_req) { #endif } +// This function sets the Input/Output capability for the BLE security. void BLESecurity::setCapability(uint8_t iocap) { + log_d("setCapability: iocap=%d", iocap); m_iocap = iocap; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); @@ -65,7 +100,12 @@ void BLESecurity::setCapability(uint8_t iocap) { #endif } +// This sets the initiator key distribution flags. +// ESP_BLE_ENC_KEY_MASK indicates that the device should distribute the Encryption Key to the peer device. +// ESP_BLE_ID_KEY_MASK indicates that the device should distribute the Identity Key to the peer device. +// Both are set by default. void BLESecurity::setInitEncryptionKey(uint8_t init_key) { + log_d("setInitEncryptionKey: init_key=%d", init_key); m_initKey = init_key; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); @@ -74,7 +114,12 @@ void BLESecurity::setInitEncryptionKey(uint8_t init_key) { #endif } +// This sets the responder key distribution flags. +// ESP_BLE_ENC_KEY_MASK indicates that the device should distribute the Encryption Key to the peer device. +// ESP_BLE_ID_KEY_MASK indicates that the device should distribute the Identity Key to the peer device. +// Both are set by default. void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { + log_d("setRespEncryptionKey: resp_key=%d", resp_key); m_respKey = resp_key; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); @@ -83,26 +128,183 @@ void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { #endif } +// This function sets the key size for the BLE security. void BLESecurity::setKeySize(uint8_t key_size) { #if defined(CONFIG_BLUEDROID_ENABLED) + log_d("setKeySize: key_size=%d", key_size); m_keySize = key_size; esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); #endif } -void BLESecurity::setStaticPIN(uint32_t pin) { - m_passkey = pin; +// This function generates a random passkey between 000000 and 999999. +uint32_t BLESecurity::generateRandomPassKey() { + return random(0, 999999); +} + +// This function sets a passkey for the BLE security. +// The first argument defines if the passkey is static or random. +// The second argument is the passkey (ignored when using a random passkey). +// The function returns the passkey that was set. +uint32_t BLESecurity::setPassKey(bool staticPasskey, uint32_t passkey) { + log_d("setPassKey: staticPasskey=%d, passkey=%d", staticPasskey, passkey); + m_staticPasskey = staticPasskey; + + if (m_staticPasskey) { + m_passkey = passkey; + if (m_passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*WARNING* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*WARNING* Please use a random passkey or set a different static passkey"); + } + } else { + m_passkey = generateRandomPassKey(); + } + + m_passkeySet = true; + #if defined(CONFIG_BLUEDROID_ENABLED) + // Workaround for making Bluedroid and NimBLE manage the random passkey similarly. esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &m_passkey, sizeof(uint32_t)); - setCapability(ESP_IO_CAP_OUT); - setKeySize(); - setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +#endif + + return m_passkey; +} + +// This function gets the passkey being used for the BLE security. +// If a static passkey is set, it will return the static passkey. +// If using a random passkey, it will generate a new random passkey if m_regenOnConnect is true. +// Otherwise, it will return the current passkey being used. +uint32_t BLESecurity::getPassKey() { + if (m_passkeySet && !m_staticPasskey && m_regenOnConnect) { + m_passkey = generateRandomPassKey(); +#if defined(CONFIG_BLUEDROID_ENABLED) + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &m_passkey, sizeof(uint32_t)); +#endif + } + return m_passkey; +} + +// This function sets if the passkey should be regenerated on each connection. +void BLESecurity::regenPassKeyOnConnect(bool enable) { + m_regenOnConnect = enable; +} + +// This function resets the security state on disconnect. +void BLESecurity::resetSecurity() { + log_d("resetSecurity: Resetting security state"); + m_securityStarted = false; + m_authenticationComplete = false; +} + +// This function sets the authentication mode with bonding, MITM, and secure connection options. +void BLESecurity::setAuthenticationMode(bool bonding, bool mitm, bool sc) { + log_d("setAuthenticationMode: bonding=%d, mitm=%d, sc=%d", bonding, mitm, sc); + m_authReq = bonding ? ESP_LE_AUTH_BOND : 0; + m_authReq |= mitm ? ESP_LE_AUTH_REQ_MITM : 0; + m_authReq |= sc ? ESP_LE_AUTH_REQ_SC_ONLY : 0; + m_securityEnabled = (m_authReq != 0); +#if defined(CONFIG_BLUEDROID_ENABLED) + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); + if (sc) { + if (mitm) { + setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_MITM); + } else { + setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + } + } #elif defined(CONFIG_NIMBLE_ENABLED) - setCapability(BLE_HS_IO_DISPLAY_ONLY); - setKeySize(); - setAuthenticationMode(false, false, true); // No bonding, no MITM, secure connection only - setInitEncryptionKey(BLE_HS_KEY_DIST_ENC_KEY | BLE_HS_KEY_DIST_ID_KEY); + ble_hs_cfg.sm_bonding = bonding; + ble_hs_cfg.sm_mitm = mitm; + ble_hs_cfg.sm_sc = sc; +#endif +} + +// This callback is called by the device that has Input capability when the peer device has Output capability +// It can also be called in NimBLE when there is no passkey set. +// It should return the passkey that the peer device is showing on its output. +// This might not be called if the client has a static passkey set. +uint32_t BLESecurityCallbacks::onPassKeyRequest() { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onPassKeyRequest."); + Serial.println("BLESecurityCallbacks: *ATTENTION* Please implement onPassKeyRequest with a suitable passkey in your BLESecurityCallbacks class"); + Serial.printf("BLESecurityCallbacks: Default passkey: %06d\n", BLE_SM_DEFAULT_PASSKEY); + return BLE_SM_DEFAULT_PASSKEY; +} + +// This callback is called by the device that has Output capability when the peer device has Input capability +// It should display the passkey that will need to be entered on the peer device +void BLESecurityCallbacks::onPassKeyNotify(uint32_t passkey) { + Serial.printf("BLESecurityCallbacks: Using default onPassKeyNotify. Passkey: %06lu\n", passkey); +} + +// This callback is called when the peer device requests a secure connection. +// Usually the client accepts the server's security request. +// It should return true if the connection is accepted, false otherwise. +bool BLESecurityCallbacks::onSecurityRequest() { + Serial.println("BLESecurityCallbacks: Using default onSecurityRequest. It will accept any security request."); + return true; +} + +// This callback is called by both devices when both have the DisplayYesNo capability. +// It should return true if both devices display the same passkey. +bool BLESecurityCallbacks::onConfirmPIN(uint32_t pin) { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onConfirmPIN. It will accept any passkey."); + Serial.println("BLESecurityCallbacks: *ATTENTION* Please implement onConfirmPIN with a suitable confirmation logic in your BLESecurityCallbacks class"); + return true; +} + +// This callback is called when the characteristic requires authorization. +// connHandle is the connection handle of the peer device. +// attrHandle is the handle of the characteristic. +// If isRead is true, the peer device is requesting to read the characteristic, +// otherwise it is requesting to write. +// It should return true if the authorization is granted, false otherwise. +bool BLESecurityCallbacks::onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onAuthorizationRequest. It will accept any authorization request."); + Serial.println( + "BLESecurityCallbacks: *ATTENTION* Please implement onAuthorizationRequest with a suitable authorization logic in your BLESecurityCallbacks class" + ); + return true; +} + +// This function waits for authentication to complete when bonding is enabled +// It prevents GATT operations from proceeding before pairing completes +void BLESecurity::waitForAuthenticationComplete(uint32_t timeoutMs) { +#if defined(CONFIG_BLUEDROID_ENABLED) + // Only wait if bonding is enabled + if ((m_authReq & ESP_LE_AUTH_BOND) == 0) { + return; + } + + // If already authenticated, no need to wait + if (m_authenticationComplete) { + return; + } + + // Semaphore should have been created in startSecurity() + if (m_authCompleteSemaphore == nullptr) { + log_e("Authentication semaphore not initialized"); + return; + } + + // Wait for authentication with timeout + bool success = m_authCompleteSemaphore->timedWait("waitForAuthenticationComplete", timeoutMs / portTICK_PERIOD_MS); + + if (!success) { + log_w("Timeout waiting for authentication to complete"); + } +#endif +} + +// This function signals that authentication has completed +// Called from ESP_GAP_BLE_AUTH_CMPL_EVT handler +void BLESecurity::signalAuthenticationComplete() { +#if defined(CONFIG_BLUEDROID_ENABLED) + m_authenticationComplete = true; + + // Signal waiting threads if semaphore exists + if (m_authCompleteSemaphore != nullptr) { + m_authCompleteSemaphore->give(); + } #endif } @@ -111,6 +313,58 @@ void BLESecurity::setStaticPIN(uint32_t pin) { ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) +// This function sets the encryption level that will be negotiated with peer device during connection +void BLESecurity::setEncryptionLevel(esp_ble_sec_act_t level) { + m_securityLevel = level; +} + +bool BLESecurity::startSecurity(esp_bd_addr_t bd_addr, int *rcPtr) { +#ifdef CONFIG_BLE_SMP_ENABLE + log_d("startSecurity: bd_addr=%s", BLEAddress(bd_addr).toString().c_str()); + if (m_securityStarted) { + log_w("Security already started for bd_addr=%s", BLEAddress(bd_addr).toString().c_str()); + if (rcPtr) { + *rcPtr = ESP_OK; + } + return true; + } + + if (m_securityEnabled) { + // Initialize semaphore before starting security to avoid race condition + if (m_authCompleteSemaphore == nullptr) { + m_authCompleteSemaphore = new FreeRTOS::Semaphore("AuthComplete"); + } + + // Reset authentication complete flag when starting new security negotiation + m_authenticationComplete = false; + + // Consume any pending semaphore signals from previous operations + // This ensures the next wait will block until the new auth completes + m_authCompleteSemaphore->take("startSecurity-reset"); + + int rc = esp_ble_set_encryption(bd_addr, m_securityLevel); + if (rc != ESP_OK) { + log_e("esp_ble_set_encryption: rc=%d %s", rc, GeneralUtils::errorToString(rc)); + } + if (rcPtr) { + *rcPtr = rc; + } + m_securityStarted = (rc == ESP_OK); + } else { + log_e("Security is not enabled. Can't start security."); + if (rcPtr) { + *rcPtr = ESP_FAIL; + } + return false; + } + return m_securityStarted; +#else + log_e("Bluedroid SMP is not enabled. Can't start security."); + return false; +#endif +} + +// This function converts an ESP BLE key type to a string representation. char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { char *key_str = nullptr; switch (key_type) { @@ -127,6 +381,12 @@ char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { } return key_str; } + +// This function is called when authentication is complete. +void BLESecurityCallbacks::onAuthenticationComplete(esp_ble_auth_cmpl_t param) { + bool success = param.success; + Serial.printf("Using default onAuthenticationComplete. Authentication %s.\n", success ? "successful" : "failed"); +} #endif /*************************************************************************** @@ -134,26 +394,42 @@ char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) -void BLESecurity::setAuthenticationMode(bool bonding, bool mitm, bool sc) { - m_authReq = bonding ? BLE_SM_PAIR_AUTHREQ_BOND : 0; - m_authReq |= mitm ? BLE_SM_PAIR_AUTHREQ_MITM : 0; - m_authReq |= sc ? BLE_SM_PAIR_AUTHREQ_SC : 0; - ble_hs_cfg.sm_bonding = bonding; - ble_hs_cfg.sm_mitm = mitm; - ble_hs_cfg.sm_sc = sc; -} - +// This function initiates security for a given connection handle. bool BLESecurity::startSecurity(uint16_t connHandle, int *rcPtr) { - int rc = ble_gap_security_initiate(connHandle); - if (rc != 0) { - log_e("ble_gap_security_initiate: rc=%d %s", rc, BLEUtils::returnCodeToString(rc)); + log_d("startSecurity: connHandle=%d", connHandle); + if (m_securityStarted) { + log_w("Security already started for connHandle=%d", connHandle); + if (rcPtr) { + *rcPtr = 0; + } + return true; } - if (rcPtr) { - *rcPtr = rc; + + if (m_securityEnabled) { + int rc = ble_gap_security_initiate(connHandle); + if (rc != 0) { + log_e("ble_gap_security_initiate: rc=%d %s", rc, BLEUtils::returnCodeToString(rc)); + } + if (rcPtr) { + *rcPtr = rc; + } + m_securityStarted = (rc == 0 || rc == BLE_HS_EALREADY); + } else { + log_e("Security is not enabled. Can't start security."); + if (rcPtr) { + *rcPtr = ESP_FAIL; + } + return false; } - return rc == 0 || rc == BLE_HS_EALREADY; + return m_securityStarted; +} + +// This function is called when authentication is complete for NimBLE. +void BLESecurityCallbacks::onAuthenticationComplete(ble_gap_conn_desc *desc) { + bool success = desc != nullptr; + Serial.printf("Using default onAuthenticationComplete. Authentication %s.\n", success ? "successful" : "failed"); } #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLESecurity.h b/libraries/BLE/src/BLESecurity.h index 574110e6118..fc6ff38f6ca 100644 --- a/libraries/BLE/src/BLESecurity.h +++ b/libraries/BLE/src/BLESecurity.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLESECURITY_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -23,6 +22,10 @@ ***************************************************************************/ #include "WString.h" +#include "BLEDevice.h" +#include "BLEClient.h" +#include "BLEServer.h" +#include "RTOS.h" /*************************************************************************** * Bluedroid includes * @@ -41,15 +44,42 @@ #endif /*************************************************************************** - * Common definitions * + * Common definitions * ***************************************************************************/ #define BLE_SM_DEFAULT_PASSKEY 123456 +/*************************************************************************** + * NimBLE definitions * + ***************************************************************************/ + +#if defined(CONFIG_NIMBLE_ENABLED) +// Compatibility with Bluedroid definitions + +#define ESP_IO_CAP_OUT BLE_HS_IO_DISPLAY_ONLY +#define ESP_IO_CAP_IO BLE_HS_IO_DISPLAY_YESNO +#define ESP_IO_CAP_IN BLE_HS_IO_KEYBOARD_ONLY +#define ESP_IO_CAP_NONE BLE_HS_IO_NO_INPUT_OUTPUT +#define ESP_IO_CAP_KBDISP BLE_HS_IO_KEYBOARD_DISPLAY + +#define ESP_LE_AUTH_NO_BOND 0x00 +#define ESP_LE_AUTH_BOND BLE_SM_PAIR_AUTHREQ_BOND +#define ESP_LE_AUTH_REQ_MITM BLE_SM_PAIR_AUTHREQ_MITM +#define ESP_LE_AUTH_REQ_SC_ONLY BLE_SM_PAIR_AUTHREQ_SC +#define ESP_LE_AUTH_REQ_BOND_MITM (BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM) +#define ESP_LE_AUTH_REQ_SC_BOND (BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_SC) +#define ESP_LE_AUTH_REQ_SC_MITM (BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC) +#define ESP_LE_AUTH_REQ_SC_MITM_BOND (BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC | BLE_SM_PAIR_AUTHREQ_BOND) + +#define ESP_BLE_ENC_KEY_MASK BLE_HS_KEY_DIST_ENC_KEY +#define ESP_BLE_ID_KEY_MASK BLE_HS_KEY_DIST_ID_KEY +#endif + /*************************************************************************** * Forward declarations * ***************************************************************************/ +class BLEDevice; class BLEServer; class BLEClient; @@ -63,13 +93,20 @@ class BLESecurity { ***************************************************************************/ BLESecurity(); - virtual ~BLESecurity(); + virtual ~BLESecurity() = default; static void setAuthenticationMode(uint8_t auth_req); static void setCapability(uint8_t iocap); - static void setInitEncryptionKey(uint8_t init_key); - static void setRespEncryptionKey(uint8_t resp_key); + static void setInitEncryptionKey(uint8_t init_key = (ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK)); + static void setRespEncryptionKey(uint8_t resp_key = (ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK)); static void setKeySize(uint8_t key_size = 16); - static void setStaticPIN(uint32_t pin); + static uint32_t setPassKey(bool staticPasskey = false, uint32_t passkey = BLE_SM_DEFAULT_PASSKEY); + static void setAuthenticationMode(bool bonding, bool mitm, bool sc); + static uint32_t getPassKey(); + static uint32_t generateRandomPassKey(); + static void regenPassKeyOnConnect(bool enable = false); + static void resetSecurity(); + static void waitForAuthenticationComplete(uint32_t timeoutMs = 10000); + static void signalAuthenticationComplete(); /*************************************************************************** * Bluedroid public declarations * @@ -77,6 +114,8 @@ class BLESecurity { #if defined(CONFIG_BLUEDROID_ENABLED) static char *esp_key_type_to_str(esp_ble_key_type_t key_type); + static void setEncryptionLevel(esp_ble_sec_act_t level); + static bool startSecurity(esp_bd_addr_t bd_addr, int *rcPtr = nullptr); #endif /*************************************************************************** @@ -84,18 +123,27 @@ class BLESecurity { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) - static void setAuthenticationMode(bool bonding, bool mitm, bool sc); static bool startSecurity(uint16_t connHandle, int *rcPtr = nullptr); #endif private: + friend class BLEDevice; friend class BLEServer; friend class BLEClient; + friend class BLERemoteCharacteristic; + friend class BLERemoteDescriptor; /*************************************************************************** * Common private properties * ***************************************************************************/ + static bool m_securityEnabled; + static bool m_securityStarted; + static bool m_forceSecurity; + static bool m_passkeySet; + static bool m_staticPasskey; + static bool m_regenOnConnect; + static bool m_authenticationComplete; static uint8_t m_iocap; static uint8_t m_authReq; static uint8_t m_initKey; @@ -103,11 +151,13 @@ class BLESecurity { static uint32_t m_passkey; /*************************************************************************** - * Bluedroid private properties * + * Bluedroid private properties * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) static uint8_t m_keySize; + static esp_ble_sec_act_t m_securityLevel; + static class FreeRTOS::Semaphore *m_authCompleteSemaphore; #endif }; // BLESecurity @@ -121,18 +171,42 @@ class BLESecurityCallbacks { * Common public declarations * ***************************************************************************/ - virtual ~BLESecurityCallbacks(){}; - virtual uint32_t onPassKeyRequest() = 0; - virtual void onPassKeyNotify(uint32_t pass_key) = 0; - virtual bool onSecurityRequest() = 0; - virtual bool onConfirmPIN(uint32_t pin) = 0; + BLESecurityCallbacks() = default; + virtual ~BLESecurityCallbacks() = default; + + // This callback is called by the device that has Input capability when the peer device has Output capability + // and the passkey is not set. + // It should return the passkey that the peer device is showing on its output. + // This MUST be replaced with a custom implementation when being used. + virtual uint32_t onPassKeyRequest(); + + // This callback is called by the device that has Output capability when the peer device has Input capability + // It should display the passkey that will need to be entered on the peer device + virtual void onPassKeyNotify(uint32_t pass_key); + + // This callback is called when the peer device requests a secure connection. + // Usually the client accepts the server's security request. + // It should return true if the connection is accepted, false otherwise. + virtual bool onSecurityRequest(); + + // This callback is called by both devices when both have the DisplayYesNo capability. + // It should return true if both devices display the same passkey. + // This MUST be replaced with a custom implementation when being used. + virtual bool onConfirmPIN(uint32_t pin); + + // This callback is called when the peer device requests authorization to read or write a characteristic. + // It should return true if the authorization is granted, false otherwise. + // This MUST be replaced with a custom implementation when being used. + virtual bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead); /*************************************************************************** * Bluedroid public declarations * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + // This callback is called when the authentication is complete. + // Status can be checked in the desc parameter. + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t desc); #endif /*************************************************************************** @@ -140,11 +214,14 @@ class BLESecurityCallbacks { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) - virtual void onAuthenticationComplete(ble_gap_conn_desc *) = 0; + // This callback is called when the authentication is complete. + // Status can be checked in the desc parameter. + virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); #endif }; // BLESecurityCallbacks -#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/libraries/BLE/src/BLEServer.cpp b/libraries/BLE/src/BLEServer.cpp index 323237e965e..b25aed2de16 100644 --- a/libraries/BLE/src/BLEServer.cpp +++ b/libraries/BLE/src/BLEServer.cpp @@ -10,16 +10,17 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#if SOC_BLE_SUPPORTED #include +#endif #include "GeneralUtils.h" #include "BLEDevice.h" #include "BLEServer.h" @@ -67,6 +68,10 @@ BLEServer::BLEServer() { m_svcChanged = false; #endif +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + m_advertiseOnDisconnect = false; +#endif + m_appId = ESP_GATT_IF_NONE; m_gattsStarted = false; m_connectedCount = 0; @@ -296,6 +301,12 @@ bool BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { return m_connectedServersMap.erase(conn_id) > 0; } +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) +void BLEServer::advertiseOnDisconnect(bool enable) { + m_advertiseOnDisconnect = enable; +} +#endif + void BLEServerCallbacks::onConnect(BLEServer *pServer) { log_d("BLEServerCallbacks", ">> onConnect(): Default"); log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); @@ -449,6 +460,16 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t if (removePeerDevice(param->disconnect.conn_id, false)) { m_connectedCount--; // Decrement the number of connected devices count. } + + // Reset security state on disconnect + BLESecurity::resetSecurity(); + + // Start advertising again if enabled + if (m_advertiseOnDisconnect) { + log_i("Start advertising again after disconnect"); + startAdvertising(); + } + break; } // ESP_GATTS_DISCONNECT_EVT @@ -621,6 +642,10 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { server->m_pServerCallbacks->onConnect(server, &desc); } + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(event->connect.conn_handle); + } + server->m_connectedCount++; } @@ -655,6 +680,16 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn); } + // Reset security state on disconnect + BLESecurity::resetSecurity(); + +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) + if (server->m_advertiseOnDisconnect) { + log_i("Start advertising again after disconnect"); + server->startAdvertising(); + } +#endif + return 0; } // BLE_GAP_EVENT_DISCONNECT @@ -784,8 +819,6 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); - } else if (server->m_pServerCallbacks != nullptr) { - server->m_pServerCallbacks->onAuthenticationComplete(&desc); } return 0; @@ -796,63 +829,124 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { struct ble_sm_io pkey = {0, 0}; if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + // Display the passkey on this device + log_d("BLE_SM_IOACT_DISP"); + pkey.action = event->passkey.params.action; - // backward compatibility - pkey.passkey = BLESecurity::m_passkey; - // if the (static)passkey is the default, check the callback for custom value - // both values default to the same. - if (pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { - if (server->m_pServerCallbacks != nullptr) { - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); - } + pkey.passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); + } + + if (BLEDevice::m_securityCallbacks != nullptr) { + BLEDevice::m_securityCallbacks->onPassKeyNotify(pkey.passkey); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + // Check if the passkey on the peer device is correct + log_d("BLE_SM_IOACT_NUMCMP"); + log_d("Passkey on device's display: %d", event->passkey.params.numcmp); pkey.action = event->passkey.params.action; - // Compatibility only - Do not use, should be removed the in future + if (BLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = BLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); - } else if (server->m_pServerCallbacks != nullptr) { - pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } else { + log_e("onConfirmPIN not implemented. Rejecting connection"); + pkey.numcmp_accept = 0; } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); - //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + // Out of band pairing + // TODO: Handle out of band pairing + log_w("BLE_SM_IOACT_OOB: Not implemented"); + static uint8_t tem_oob[16] = {0}; pkey.action = event->passkey.params.action; for (int i = 0; i < 16; i++) { pkey.oob[i] = tem_oob[i]; } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { - log_d("Enter the passkey"); + // Input passkey from peer device + log_d("BLE_SM_IOACT_INPUT"); + pkey.action = event->passkey.params.action; + pkey.passkey = BLESecurity::getPassKey(); - // Compatibility only - Do not use, should be removed the in future - if (BLEDevice::m_securityCallbacks != nullptr) { - pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); - } else if (server->m_pServerCallbacks != nullptr) { - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } + } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { - log_d("No passkey action required"); + log_d("BLE_SM_IOACT_NONE"); + log_i("No passkey action required"); } log_d("<< handleGATTServerEvent"); return 0; } // BLE_GAP_EVENT_PASSKEY_ACTION + case BLE_GAP_EVENT_AUTHORIZE: + { + log_d("BLE_GAP_EVENT_AUTHORIZE"); + + log_i( + "Authorization request: conn_handle=%d attr_handle=%d is_read=%d", event->authorize.conn_handle, event->authorize.attr_handle, event->authorize.is_read + ); + + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Asking for authorization from onAuthorizationRequest"); + authorized = + BLEDevice::m_securityCallbacks->onAuthorizationRequest(event->authorize.conn_handle, event->authorize.attr_handle, event->authorize.is_read); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting authorization request"); + } + + if (authorized) { + log_i("Authorization granted"); + event->authorize.out_response = BLE_GAP_AUTHORIZE_ACCEPT; + } else { + log_i("Authorization rejected"); + event->authorize.out_response = BLE_GAP_AUTHORIZE_REJECT; + } + + return 0; + } // BLE_GAP_EVENT_AUTHORIZE + default: break; } @@ -951,21 +1045,7 @@ void BLEServerCallbacks::onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *des log_d("BLEServerCallbacks", "<< onMtuChanged()"); } // onMtuChanged -uint32_t BLEServerCallbacks::onPassKeyRequest() { - log_d("BLEServerCallbacks", "onPassKeyRequest: default: 123456"); - return 123456; -} - -void BLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc *) { - log_d("BLEServerCallbacks", "onAuthenticationComplete: default"); -} - -bool BLEServerCallbacks::onConfirmPIN(uint32_t pin) { - log_d("BLEServerCallbacks", "onConfirmPIN: default: true"); - return true; -} - #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEServer.h b/libraries/BLE/src/BLEServer.h index 865d1046312..c385c22cc98 100644 --- a/libraries/BLE/src/BLEServer.h +++ b/libraries/BLE/src/BLEServer.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_ #define COMPONENTS_CPP_UTILS_BLESERVER_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -129,6 +129,9 @@ class BLEServer { BLEService *getServiceByUUID(const char *uuid); BLEService *getServiceByUUID(BLEUUID uuid); void start(); +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + void advertiseOnDisconnect(bool enable); +#endif // Connection management functions std::map getPeerDevices(bool client); @@ -174,6 +177,9 @@ class BLEServer { uint32_t m_connectedCount; bool m_gattsStarted; std::map m_connectedServersMap; +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + bool m_advertiseOnDisconnect; +#endif FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); @@ -261,12 +267,10 @@ class BLEServerCallbacks { virtual void onConnect(BLEServer *pServer, ble_gap_conn_desc *desc); virtual void onDisconnect(BLEServer *pServer, ble_gap_conn_desc *desc); virtual void onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *desc, uint16_t mtu); - virtual uint32_t onPassKeyRequest(); - virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); - virtual bool onConfirmPIN(uint32_t pin); #endif }; // BLEServerCallbacks #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ diff --git a/libraries/BLE/src/BLEService.cpp b/libraries/BLE/src/BLEService.cpp index 60449719b6a..438c9aa6a1b 100644 --- a/libraries/BLE/src/BLEService.cpp +++ b/libraries/BLE/src/BLEService.cpp @@ -12,9 +12,8 @@ // A service is identified by a UUID. A service is also the container for one or more characteristics. #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -650,4 +649,4 @@ bool BLEService::start() { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEService.h b/libraries/BLE/src/BLEService.h index 67088b60310..a3d3f14b2cd 100644 --- a/libraries/BLE/src/BLEService.h +++ b/libraries/BLE/src/BLEService.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ #define COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -61,13 +61,13 @@ class BLECharacteristicMap { void setByUUID(BLECharacteristic *pCharacteristic, const char *uuid); void setByUUID(BLECharacteristic *pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic *pCharacteristic); - BLECharacteristic *getByUUID(const char *uuid); - BLECharacteristic *getByUUID(BLEUUID uuid); - BLECharacteristic *getByHandle(uint16_t handle); + BLECharacteristic *getByUUID(const char *uuid) const; + BLECharacteristic *getByUUID(BLEUUID uuid) const; + BLECharacteristic *getByHandle(uint16_t handle) const; BLECharacteristic *getFirst(); BLECharacteristic *getNext(); - String toString(); - int getRegisteredCharacteristicCount(); + String toString() const; + int getRegisteredCharacteristicCount() const; void removeCharacteristic(BLECharacteristic *characteristic); /*************************************************************************** @@ -197,5 +197,6 @@ class BLEService { }; // BLEService #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ diff --git a/libraries/BLE/src/BLEServiceMap.cpp b/libraries/BLE/src/BLEServiceMap.cpp index 477da5b4cc2..ee58aa284e3 100644 --- a/libraries/BLE/src/BLEServiceMap.cpp +++ b/libraries/BLE/src/BLEServiceMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -154,4 +153,4 @@ void BLEServiceMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_i #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUUID.cpp b/libraries/BLE/src/BLEUUID.cpp index d61fb695799..5dd3833b0e4 100644 --- a/libraries/BLE/src/BLEUUID.cpp +++ b/libraries/BLE/src/BLEUUID.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -333,4 +332,4 @@ const ble_uuid_any_t *BLEUUID::getNative() const { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUUID.h b/libraries/BLE/src/BLEUUID.h index 43c4e91b01d..da7c38928f3 100644 --- a/libraries/BLE/src/BLEUUID.h +++ b/libraries/BLE/src/BLEUUID.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_ #define COMPONENTS_CPP_UTILS_BLEUUID_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -123,5 +123,6 @@ class BLEUUID { }; // BLEUUID #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/libraries/BLE/src/BLEUtils.cpp b/libraries/BLE/src/BLEUtils.cpp index 4ca04e6e2b6..f4b3ac6d217 100644 --- a/libraries/BLE/src/BLEUtils.cpp +++ b/libraries/BLE/src/BLEUtils.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -27,7 +26,9 @@ #include #include +#if SOC_BLE_SUPPORTED #include +#endif #include #include @@ -656,7 +657,7 @@ static const gattService_t g_gattServices[] = { * @param [in] length The length of the data to convert. * @return A pointer to the formatted buffer. */ -char *BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { +char *BLEUtils::buildHexData(uint8_t *target, const uint8_t *source, uint8_t length) { // Guard against too much data. if (length > 100) { length = 100; @@ -672,8 +673,7 @@ char *BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { char *startOfData = (char *)target; for (int i = 0; i < length; i++) { - sprintf((char *)target, "%.2x", (char)*source); - source++; + sprintf((char *)target, "%.2x", (char)source[i]); target += 2; } @@ -1568,7 +1568,7 @@ void BLEUtils::dumpGattServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gat // - uint32_t trans_id // - esp_bd_addr_t bda // - uint8_t exec_write_flag -#ifdef ARDUHAL_LOG_LEVEL_VERBOSE +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE case ESP_GATTS_EXEC_WRITE_EVT: { char *pWriteFlagText; @@ -2171,6 +2171,9 @@ const char *BLEUtils::gapEventToString(uint8_t eventType) { case BLE_GAP_EVENT_EXT_DISC: //19 return "BLE_GAP_EVENT_EXT_DISC"; + + case BLE_GAP_EVENT_AUTHORIZE: //32 + return "BLE_GAP_EVENT_AUTHORIZE"; #ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these case BLE_GAP_EVENT_PERIODIC_SYNC: //20 return "BLE_GAP_EVENT_PERIODIC_SYNC"; @@ -2254,4 +2257,4 @@ void BLEUtils::taskRelease(const BLETaskData &taskData, int flags) { #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUtils.h b/libraries/BLE/src/BLEUtils.h index 2689706b7a1..f66cdcfcdb1 100644 --- a/libraries/BLE/src/BLEUtils.h +++ b/libraries/BLE/src/BLEUtils.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_ #define COMPONENTS_CPP_UTILS_BLEUTILS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -96,7 +96,7 @@ class BLEUtils { * Common public declarations * ***************************************************************************/ - static char *buildHexData(uint8_t *target, uint8_t *source, uint8_t length); + static char *buildHexData(uint8_t *target, const uint8_t *source, uint8_t length); static String buildPrintData(uint8_t *source, size_t length); static const char *advDataTypeToString(uint8_t advType); static String characteristicPropertiesToString(uint8_t prop); @@ -148,5 +148,6 @@ class BLEUtils { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */ diff --git a/libraries/BLE/src/BLEValue.cpp b/libraries/BLE/src/BLEValue.cpp index efc97697baa..47a7b3d9a84 100644 --- a/libraries/BLE/src/BLEValue.cpp +++ b/libraries/BLE/src/BLEValue.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -37,7 +36,7 @@ BLEValue::BLEValue() { * The accumulation is a growing set of data that is added to until a commit or cancel. * @param [in] part A message part being added. */ -void BLEValue::addPart(String part) { +void BLEValue::addPart(const String &part) { log_v(">> addPart: length=%d", part.length()); m_accumulation += part; } // addPart @@ -48,7 +47,7 @@ void BLEValue::addPart(String part) { * @param [in] pData A message part being added. * @param [in] length The number of bytes being added. */ -void BLEValue::addPart(uint8_t *pData, size_t length) { +void BLEValue::addPart(const uint8_t *pData, size_t length) { log_v(">> addPart: length=%d", length); m_accumulation += String((char *)pData, length); } // addPart @@ -91,7 +90,7 @@ uint8_t *BLEValue::getData() { * @brief Get the length of the data in bytes. * @return The length of the data in bytes. */ -size_t BLEValue::getLength() { +size_t BLEValue::getLength() const { return m_value.length(); } // getLength @@ -99,14 +98,14 @@ size_t BLEValue::getLength() { * @brief Get the read offset. * @return The read offset into the read. */ -uint16_t BLEValue::getReadOffset() { +uint16_t BLEValue::getReadOffset() const { return m_readOffset; } // getReadOffset /** * @brief Get the current value. */ -String BLEValue::getValue() { +String BLEValue::getValue() const { return m_value; } // getValue @@ -121,7 +120,7 @@ void BLEValue::setReadOffset(uint16_t readOffset) { /** * @brief Set the current value. */ -void BLEValue::setValue(String value) { +void BLEValue::setValue(const String &value) { m_value = value; } // setValue @@ -130,9 +129,9 @@ void BLEValue::setValue(String value) { * @param [in] pData The data for the current value. * @param [in] The length of the new current value. */ -void BLEValue::setValue(uint8_t *pData, size_t length) { +void BLEValue::setValue(const uint8_t *pData, size_t length) { m_value = String((char *)pData, length); } // setValue #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEValue.h b/libraries/BLE/src/BLEValue.h index 56a7a5bc4ec..99147db0959 100644 --- a/libraries/BLE/src/BLEValue.h +++ b/libraries/BLE/src/BLEValue.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLEVALUE_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -34,17 +33,17 @@ class BLEValue { ***************************************************************************/ BLEValue(); - void addPart(String part); - void addPart(uint8_t *pData, size_t length); + void addPart(const String &part); + void addPart(const uint8_t *pData, size_t length); void cancel(); void commit(); uint8_t *getData(); - size_t getLength(); - uint16_t getReadOffset(); - String getValue(); + size_t getLength() const; + uint16_t getReadOffset() const; + String getValue() const; void setReadOffset(uint16_t readOffset); - void setValue(String value); - void setValue(uint8_t *pData, size_t length); + void setValue(const String &value); + void setValue(const uint8_t *pData, size_t length); private: /*************************************************************************** @@ -57,5 +56,6 @@ class BLEValue { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ diff --git a/libraries/BLE/src/HIDKeyboardTypes.h b/libraries/BLE/src/HIDKeyboardTypes.h index 971d637f961..e0b40ebafc3 100644 --- a/libraries/BLE/src/HIDKeyboardTypes.h +++ b/libraries/BLE/src/HIDKeyboardTypes.h @@ -21,14 +21,28 @@ #ifndef KEYBOARD_DEFS_H #define KEYBOARD_DEFS_H +#include "esp_bit_defs.h" + #define REPORT_ID_KEYBOARD 1 #define REPORT_ID_VOLUME 3 /* Modifiers */ enum MODIFIER_KEY { - KEY_CTRL = 1, - KEY_SHIFT = 2, - KEY_ALT = 4, + /* Aliases for the left modifiers */ + KEY_CTRL = BIT(0), + KEY_SHIFT = BIT(1), + KEY_ALT = BIT(2), + KEY_GUI = BIT(3), /*!< GUI key (Command on macOS, Windows key on Windows) */ + /* Left modifiers */ + KEY_LEFT_CTRL = BIT(0), + KEY_LEFT_SHIFT = BIT(1), + KEY_LEFT_ALT = BIT(2), + KEY_LEFT_GUI = BIT(3), + /* Right modifiers */ + KEY_RIGHT_CTRL = BIT(4), + KEY_RIGHT_SHIFT = BIT(5), + KEY_RIGHT_ALT = BIT(6), + KEY_RIGHT_GUI = BIT(7), }; enum MEDIA_KEY { diff --git a/libraries/BluetoothSerial/examples/DiscoverConnect/ci.json b/libraries/BluetoothSerial/examples/DiscoverConnect/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/DiscoverConnect/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/DiscoverConnect/ci.yml b/libraries/BluetoothSerial/examples/DiscoverConnect/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/DiscoverConnect/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/GetLocalMAC/ci.json b/libraries/BluetoothSerial/examples/GetLocalMAC/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/GetLocalMAC/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/GetLocalMAC/ci.yml b/libraries/BluetoothSerial/examples/GetLocalMAC/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/GetLocalMAC/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.json b/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.yml b/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.json b/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.yml b/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBTM/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.json b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.yml b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT_Legacy/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.json b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.yml b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/SerialToSerialBT_SSP/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.json b/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.yml b/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/bt_classic_device_discovery/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.json b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.json deleted file mode 100644 index b5097688f52..00000000000 --- a/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_BT_SPP_ENABLED=y" - ] -} diff --git a/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.yml b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.yml new file mode 100644 index 00000000000..335e5be5b76 --- /dev/null +++ b/libraries/BluetoothSerial/examples/bt_remove_paired_devices/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_BT_SPP_ENABLED=y diff --git a/libraries/BluetoothSerial/library.properties b/libraries/BluetoothSerial/library.properties index 49211bf3b63..03d5687a49e 100644 --- a/libraries/BluetoothSerial/library.properties +++ b/libraries/BluetoothSerial/library.properties @@ -1,5 +1,5 @@ name=BluetoothSerial -version=3.3.0 +version=3.3.4 author=Evandro Copercini maintainer=Evandro Copercini sentence=Simple UART to Classical Bluetooth bridge for ESP32 diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.h b/libraries/BluetoothSerial/src/BluetoothSerial.h index 8cb6edb876c..68ddd3c747a 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.h +++ b/libraries/BluetoothSerial/src/BluetoothSerial.h @@ -35,7 +35,7 @@ typedef std::function KeyRequestCb; typedef std::function AuthCompleteCb; typedef std::function BTAdvertisedDeviceCb; -class BluetoothSerial : public Stream { +class [[deprecated("BluetoothSerial won't be supported in version 4.0.0 by default")]] BluetoothSerial : public Stream { public: BluetoothSerial(void); ~BluetoothSerial(void); diff --git a/libraries/DNSServer/examples/CaptivePortal/ci.json b/libraries/DNSServer/examples/CaptivePortal/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/DNSServer/examples/CaptivePortal/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/DNSServer/examples/CaptivePortal/ci.yml b/libraries/DNSServer/examples/CaptivePortal/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/DNSServer/examples/CaptivePortal/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/DNSServer/library.properties b/libraries/DNSServer/library.properties index c193b919d02..6d124cace69 100644 --- a/libraries/DNSServer/library.properties +++ b/libraries/DNSServer/library.properties @@ -1,5 +1,5 @@ name=DNSServer -version=3.3.0 +version=3.3.4 author=Kristijan Novoselić maintainer=Kristijan Novoselić, sentence=A simple DNS server for ESP32. diff --git a/libraries/DNSServer/src/DNSServer.cpp b/libraries/DNSServer/src/DNSServer.cpp index 1f74c96c733..795e735b4fa 100644 --- a/libraries/DNSServer/src/DNSServer.cpp +++ b/libraries/DNSServer/src/DNSServer.cpp @@ -132,7 +132,7 @@ bool DNSServer::requestIncludesOnlyOneQuestion(DNSHeader &dnsHeader) { String DNSServer::getDomainNameWithoutWwwPrefix(const unsigned char *start, size_t len) { String parsedDomainName(start, --len); // exclude trailing null byte from labels length, String constructor will add it anyway - int pos = 0; + size_t pos = 0; while (pos < len) { parsedDomainName.setCharAt(pos, 0x2e); // replace label len byte with dot char "." pos += *(start + pos); diff --git a/libraries/EEPROM/library.properties b/libraries/EEPROM/library.properties index 6d69f52a085..bf03f1a66e1 100644 --- a/libraries/EEPROM/library.properties +++ b/libraries/EEPROM/library.properties @@ -1,5 +1,5 @@ name=EEPROM -version=3.3.0 +version=3.3.4 author=Ivan Grokhotkov maintainer=Paolo Becchi sentence=Enables reading and writing data a sequential, addressable FLASH storage diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp index 016e6843dd2..05b9d3acd44 100644 --- a/libraries/EEPROM/src/EEPROM.cpp +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -27,6 +27,7 @@ #include #include #include +#include EEPROMClass::EEPROMClass(void) : _handle(0), _data(0), _size(0), _dirty(false), _name("eeprom") {} @@ -59,7 +60,7 @@ bool EEPROMClass::begin(size_t size) { } if (size < key_size) { // truncate log_w("truncating EEPROM from %d to %d", key_size, size); - uint8_t *key_data = (uint8_t *)malloc(key_size); + uint8_t *key_data = new (std::nothrow) uint8_t[key_size]; if (!key_data) { log_e("Not enough memory to truncate EEPROM!"); return false; @@ -67,10 +68,10 @@ bool EEPROMClass::begin(size_t size) { nvs_get_blob(_handle, _name, key_data, &key_size); nvs_set_blob(_handle, _name, key_data, size); nvs_commit(_handle); - free(key_data); + delete[] key_data; } else if (size > key_size) { // expand or new size_t expand_size = size - key_size; - uint8_t *expand_key = (uint8_t *)malloc(expand_size); + uint8_t *expand_key = new (std::nothrow) uint8_t[expand_size]; if (!expand_key) { log_e("Not enough memory to expand EEPROM!"); return false; @@ -78,12 +79,12 @@ bool EEPROMClass::begin(size_t size) { // check for adequate free space if (nvs_set_blob(_handle, "expand", expand_key, expand_size)) { log_e("Not enough space to expand EEPROM from %d to %d", key_size, size); - free(expand_key); + delete[] expand_key; return false; } - free(expand_key); + delete[] expand_key; nvs_erase_key(_handle, "expand"); - uint8_t *key_data = (uint8_t *)malloc(size); + uint8_t *key_data = new (std::nothrow) uint8_t[size]; if (!key_data) { log_e("Not enough memory to expand EEPROM!"); return false; @@ -99,7 +100,7 @@ bool EEPROMClass::begin(size_t size) { } nvs_commit(_handle); nvs_set_blob(_handle, _name, key_data, size); - free(key_data); + delete[] key_data; nvs_commit(_handle); } @@ -107,7 +108,7 @@ bool EEPROMClass::begin(size_t size) { delete[] _data; } - _data = (uint8_t *)malloc(size); + _data = new (std::nothrow) uint8_t[size]; if (!_data) { log_e("Not enough memory for %d bytes in EEPROM", size); return false; @@ -212,7 +213,7 @@ uint16_t EEPROMClass::convert(bool clear, const char *EEPROMname, const char *nv } size_t size = mypart->size; - uint8_t *data = (uint8_t *)malloc(size); + uint8_t *data = new (std::nothrow) uint8_t[size]; if (!data) { log_e("Not enough memory to convert EEPROM!"); goto exit; @@ -255,7 +256,7 @@ uint16_t EEPROMClass::convert(bool clear, const char *EEPROMname, const char *nv } } exit: - free(data); + delete[] data; return result; } @@ -509,9 +510,9 @@ size_t EEPROMClass::writeBytes(int address, const void *value, size_t len) { return len; } -template T EEPROMClass::writeAll(int address, const T &value) { +template size_t EEPROMClass::writeAll(int address, const T &value) { if (address < 0 || address + sizeof(T) > _size) { - return value; + return 0; } memcpy(_data + address, (const uint8_t *)&value, sizeof(T)); diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h index 2bcc97a3a21..45222b0c697 100644 --- a/libraries/EEPROM/src/EEPROM.h +++ b/libraries/EEPROM/src/EEPROM.h @@ -105,7 +105,7 @@ class EEPROMClass { size_t writeString(int address, const char *value); size_t writeString(int address, String value); size_t writeBytes(int address, const void *value, size_t len); - template T writeAll(int address, const T &); + template size_t writeAll(int address, const T &); protected: nvs_handle _handle; diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json deleted file mode 100644 index a9d8603b7bf..00000000000 --- a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED=y" - ] -} diff --git a/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.yml b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.yml new file mode 100644 index 00000000000..b001ab0f4a3 --- /dev/null +++ b/libraries/ESP32/examples/AnalogOut/LEDCGammaFade/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_LEDC_GAMMA_CURVE_FADE_SUPPORTED=y diff --git a/libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino b/libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino index 5011bebe798..80c057a5dc7 100644 --- a/libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino +++ b/libraries/ESP32/examples/AnalogReadContinuous/AnalogReadContinuous.ino @@ -16,7 +16,7 @@ uint8_t adc_pins_count = sizeof(adc_pins) / sizeof(uint8_t); volatile bool adc_coversion_done = false; // Result structure for ADC Continuous reading -adc_continuous_data_t *result = NULL; +adc_continuous_result_t *result = NULL; // ISR Function that will be triggered when ADC conversion is done void ARDUINO_ISR_ATTR adcComplete() { diff --git a/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino b/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino new file mode 100644 index 00000000000..7b23e259173 --- /dev/null +++ b/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino @@ -0,0 +1,13 @@ +// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds +// before the sketch would start its execution. It gives the user time to open the Serial Monitor + +// This will force the Sketch execution to wait for 5 seconds before starting it execution +// setup() will be executed only after this time +SET_TIME_BEFORE_STARTING_SKETCH_MS(5000); + +void setup() { + Serial.begin(115200); + Serial.println("After 5 seconds... this message will be seen in the Serial Monitor."); +} + +void loop() {} diff --git a/libraries/ESP32/examples/Camera/CameraWebServer/ci.json b/libraries/ESP32/examples/Camera/CameraWebServer/ci.json deleted file mode 100644 index 35c3056dda8..00000000000 --- a/libraries/ESP32/examples/Camera/CameraWebServer/ci.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "fqbn": { - "esp32": [ - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=custom,FlashMode=dio", - "espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=custom,FlashMode=dio" - ], - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=custom,FlashMode=dio", - "espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=custom,FlashMode=dio" - ], - "esp32s3": [ - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=custom,FlashMode=qio", - "espressif:esp32:esp32s3:PSRAM=enabled,USBMode=default,PartitionScheme=custom,FlashMode=qio", - "espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=custom,FlashMode=qio" - ] - }, - "requires": [ - "CONFIG_CAMERA_TASK_STACK_SIZE=[0-9]+" - ] -} diff --git a/libraries/ESP32/examples/Camera/CameraWebServer/ci.yml b/libraries/ESP32/examples/Camera/CameraWebServer/ci.yml new file mode 100644 index 00000000000..aea91ac431c --- /dev/null +++ b/libraries/ESP32/examples/Camera/CameraWebServer/ci.yml @@ -0,0 +1,14 @@ +fqbn: + esp32: + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=custom,FlashMode=dio + - espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=custom,FlashMode=dio + esp32s2: + - espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=custom,FlashMode=dio + - espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=custom,FlashMode=dio + esp32s3: + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=custom,FlashMode=qio + - espressif:esp32:esp32s3:PSRAM=enabled,USBMode=default,PartitionScheme=custom,FlashMode=qio + - espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=custom,FlashMode=qio + +requires: + - CONFIG_CAMERA_TASK_STACK_SIZE=[0-9]+ diff --git a/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.json b/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.json deleted file mode 100644 index dfd49d94fe9..00000000000 --- a/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "targets": { - "esp32c3": false, - "esp32c6": false, - "esp32h2": false, - "esp32p4": false, - "esp32c5": false - } -} diff --git a/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.yml b/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.yml new file mode 100644 index 00000000000..f0c2d6e9fe2 --- /dev/null +++ b/libraries/ESP32/examples/DeepSleep/ExternalWakeUp/ci.yml @@ -0,0 +1,6 @@ +targets: + esp32c3: false + esp32c6: false + esp32h2: false + esp32p4: false + esp32c5: false diff --git a/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.json b/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.json deleted file mode 100644 index 5fa2bd14e5d..00000000000 --- a/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "targets": { - "esp32c3": false, - "esp32c6": false, - "esp32h2": false, - "esp32p4": false, - "esp32s2": false, - "esp32s3": false, - "esp32c5": false - } -} diff --git a/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.yml b/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.yml new file mode 100644 index 00000000000..556ac03be4c --- /dev/null +++ b/libraries/ESP32/examples/DeepSleep/SmoothBlink_ULP_Code/ci.yml @@ -0,0 +1,8 @@ +targets: + esp32c3: false + esp32c6: false + esp32h2: false + esp32p4: false + esp32s2: false + esp32s3: false + esp32c5: false diff --git a/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.json b/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.json deleted file mode 100644 index d8b3664bc65..00000000000 --- a/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "targets": { - "esp32h2": false - } -} diff --git a/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.yml b/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.yml new file mode 100644 index 00000000000..f2c7072e1c2 --- /dev/null +++ b/libraries/ESP32/examples/DeepSleep/TimerWakeUp/ci.yml @@ -0,0 +1,2 @@ +targets: + esp32h2: false diff --git a/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.json b/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.json deleted file mode 100644 index ae65fa0df74..00000000000 --- a/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "targets": { - "esp32c3": false, - "esp32c6": false, - "esp32h2": false, - "esp32c5": false - } -} diff --git a/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.yml b/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.yml new file mode 100644 index 00000000000..dc765ea8326 --- /dev/null +++ b/libraries/ESP32/examples/DeepSleep/TouchWakeUp/ci.yml @@ -0,0 +1,5 @@ +targets: + esp32c3: false + esp32c6: false + esp32h2: false + esp32c5: false diff --git a/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino b/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino index f368e0e864c..9c90571493d 100644 --- a/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino +++ b/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino @@ -71,17 +71,17 @@ void Task(void *pvParameters) { // This is a task. #endif int new_value = random(1000); - char str0[32]; - sprintf(str0, " %d <- %d |", shared_variable, new_value); - char str1[32]; - sprintf(str1, " | %d <- %d", shared_variable, new_value); + char str0[35]; // Maximum possible length of the string + snprintf(str0, sizeof(str0), " %d <- %d |", shared_variable, new_value); + char str1[46]; // Maximum possible length of the string + snprintf(str1, sizeof(str1), " | %d <- %d", shared_variable, new_value); Serial.printf("%s\n", task_num ? str0 : str1); shared_variable = new_value; delay(random(100)); // wait random time of max 100 ms - simulating some computation - sprintf(str0, " R: %d |", shared_variable); - sprintf(str1, " | R: %d", shared_variable); + snprintf(str0, sizeof(str0), " R: %d |", shared_variable); + snprintf(str1, sizeof(str1), " | R: %d", shared_variable); Serial.printf("%s\n", task_num ? str0 : str1); //Serial.printf("Task %d after write: reading %d\n", task_num, shared_variable); diff --git a/libraries/ESP32/examples/HWCDC_Events/ci.json b/libraries/ESP32/examples/HWCDC_Events/ci.json deleted file mode 100644 index 56e38bbcdf2..00000000000 --- a/libraries/ESP32/examples/HWCDC_Events/ci.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fqbn": { - "esp32s3": [ - "espressif:esp32:esp32s3:USBMode=hwcdc,PartitionScheme=huge_app,FlashMode=dio" - ] - }, - "requires": [ - "CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y" - ] -} diff --git a/libraries/ESP32/examples/HWCDC_Events/ci.yml b/libraries/ESP32/examples/HWCDC_Events/ci.yml new file mode 100644 index 00000000000..6561aa854a2 --- /dev/null +++ b/libraries/ESP32/examples/HWCDC_Events/ci.yml @@ -0,0 +1,6 @@ +fqbn: + esp32s3: + - espressif:esp32:esp32s3:USBMode=hwcdc,PartitionScheme=huge_app,FlashMode=dio + +requires: + - CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y diff --git a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino index 1cdd2224ea5..df906588479 100644 --- a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino +++ b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino @@ -276,6 +276,30 @@ void RMT_Loop_Write_Blink() { delay(2000); } +void RMT_Repeated_Write_Blink() { + Serial.println("Using RMT Writing repeated N times to blink an LED."); + Serial.println("Blinking at 1s on + 1s off :: 2 blinks"); + // repeating blink_1s_rmt_data (1s on + 1s off) 2 times for 2 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data), 2)) { + Serial.println("===> rmtWrite Blink 1s Error!"); + } + delay(4000 + 1000); // it should blink 2 times and stop automatically + Serial.println("Blinking at 500ms on + 500ms off :: 4 blinks"); + // repeating blink_500ms_rmt_data (500ms on + 500ms off) 4 times for 4 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data), 4)) { + Serial.println("===> rmtWrite Blink 0.5s Error!"); + } + delay(4000 + 1000); // it should blink 4 times and stop automatically + Serial.println("Blinking at 250ms on + 250ms off :: 8 blinks"); + // repeating blink_250ms_rmt_data (250ms on + 250ms off) 8 times for 8 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data), 8)) { + Serial.println("===> rmtWrite Blink 0.25s Error!"); + } + delay(4000 + 1000); // it should blink 8 times and stop automatically + Serial.println("Blinking is OFF for 2 seconds"); + delay(2000); +} + void RMT_Single_Write_Blocking_Blink() { Serial.println("Using RMT Writing and its Completion to blink an LED."); Serial.println("Blinking at 1s on + 1s off :: 2 blinks"); @@ -356,6 +380,7 @@ void setup() { void loop() { RMT_Write_Aync_Non_Blocking_Blink(); RMT_Loop_Write_Blink(); + RMT_Repeated_Write_Blink(); RMT_Single_Write_Blocking_Blink(); Serial.println("\nStarting OVER...\n"); } diff --git a/libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino b/libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino index ca7e15bf479..fe047b242e1 100644 --- a/libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino +++ b/libraries/ESP32/examples/ResetReason/ResetReason/ResetReason.ino @@ -30,6 +30,8 @@ #include "esp32p4/rom/rtc.h" #elif CONFIG_IDF_TARGET_ESP32C5 #include "esp32c5/rom/rtc.h" +#elif CONFIG_IDF_TARGET_ESP32C61 +#include "esp32c61/rom/rtc.h" #else #error Target CONFIG_IDF_TARGET is not supported #endif diff --git a/libraries/ESP32/examples/TWAI/TWAIreceive/ci.json b/libraries/ESP32/examples/TWAI/TWAIreceive/ci.json deleted file mode 100644 index 7379dba8bb9..00000000000 --- a/libraries/ESP32/examples/TWAI/TWAIreceive/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "targets": { - "esp32c5": false - } -} diff --git a/libraries/ESP32/examples/TWAI/TWAIreceive/ci.yml b/libraries/ESP32/examples/TWAI/TWAIreceive/ci.yml new file mode 100644 index 00000000000..655244ef541 --- /dev/null +++ b/libraries/ESP32/examples/TWAI/TWAIreceive/ci.yml @@ -0,0 +1,2 @@ +targets: + esp32c5: false diff --git a/libraries/ESP32/examples/TWAI/TWAItransmit/ci.json b/libraries/ESP32/examples/TWAI/TWAItransmit/ci.json deleted file mode 100644 index 7379dba8bb9..00000000000 --- a/libraries/ESP32/examples/TWAI/TWAItransmit/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "targets": { - "esp32c5": false - } -} diff --git a/libraries/ESP32/examples/TWAI/TWAItransmit/ci.yml b/libraries/ESP32/examples/TWAI/TWAItransmit/ci.yml new file mode 100644 index 00000000000..655244ef541 --- /dev/null +++ b/libraries/ESP32/examples/TWAI/TWAItransmit/ci.yml @@ -0,0 +1,2 @@ +targets: + esp32c5: false diff --git a/libraries/ESP32/examples/Time/SimpleTime/ci.json b/libraries/ESP32/examples/Time/SimpleTime/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/ESP32/examples/Time/SimpleTime/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/ESP32/examples/Time/SimpleTime/ci.yml b/libraries/ESP32/examples/Time/SimpleTime/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/ESP32/examples/Time/SimpleTime/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino b/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino index 43f3bc36592..ac2b641be40 100644 --- a/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino +++ b/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino @@ -3,44 +3,53 @@ This is an example how to use Touch Intrrerupts The sketch will tell when it is touched and then released as like a push-button -This method based on touchInterruptSetThresholdDirection() is only available for ESP32 +This method based on touchInterruptGetLastStatus() */ #include "Arduino.h" -int threshold = 40; -bool touchActive = false; -bool lastTouchActive = false; -bool testingLower = true; - -void gotTouchEvent() { - if (lastTouchActive != testingLower) { - touchActive = !touchActive; - testingLower = !testingLower; - // Touch ISR will be inverted: Lower <--> Higher than the Threshold after ISR event is noticed - touchInterruptSetThresholdDirection(testingLower); - } +int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) +bool touch1detected = false; +bool touch2detected = false; + +void gotTouch1() { + touch1detected = true; +} + +void gotTouch2() { + touch2detected = true; } void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor - Serial.println("ESP32 Touch Interrupt Test"); - touchAttachInterrupt(T2, gotTouchEvent, threshold); - // Touch ISR will be activated when touchRead is lower than the Threshold - touchInterruptSetThresholdDirection(testingLower); + //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. + touchSetDefaultThreshold(5); + + //Set the touch pads + Serial.println("\n ESP32 Touch Interrupt Test\n"); + touchAttachInterrupt(T1, gotTouch1, threshold); + touchAttachInterrupt(T2, gotTouch2, threshold); } void loop() { - if (lastTouchActive != touchActive) { - lastTouchActive = touchActive; - if (touchActive) { - Serial.println(" ---- Touch was Pressed"); + if (touch1detected) { + touch1detected = false; + if (touchInterruptGetLastStatus(T1)) { + Serial.println(" --- T1 Touched"); } else { - Serial.println(" ---- Touch was Released"); + Serial.println(" --- T1 Released"); } } - Serial.printf("T2 pin2 = %d \n", touchRead(T2)); - delay(125); + if (touch2detected) { + touch2detected = false; + if (touchInterruptGetLastStatus(T2)) { + Serial.println(" --- T2 Touched"); + } else { + Serial.println(" --- T2 Released"); + } + } + + delay(80); } diff --git a/libraries/ESP32/examples/Touch/TouchButton/ci.json b/libraries/ESP32/examples/Touch/TouchButton/ci.json deleted file mode 100644 index cec76a84f9d..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButton/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_VERSION_1=y" - ] -} diff --git a/libraries/ESP32/examples/Touch/TouchButton/ci.yml b/libraries/ESP32/examples/Touch/TouchButton/ci.yml new file mode 100644 index 00000000000..feaef91cf65 --- /dev/null +++ b/libraries/ESP32/examples/Touch/TouchButton/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y diff --git a/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino b/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino deleted file mode 100644 index df9b3f41149..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino +++ /dev/null @@ -1,51 +0,0 @@ -/* - -This is an example how to use Touch Intrrerupts -The sketch will tell when it is touched and then released as like a push-button - -This method based on touchInterruptGetLastStatus() is only available for ESP32 S2 and S3 -*/ - -#include "Arduino.h" - -int threshold = 1500; // ESP32S2 -bool touch1detected = false; -bool touch2detected = false; - -void gotTouch1() { - touch1detected = true; -} - -void gotTouch2() { - touch2detected = true; -} - -void setup() { - Serial.begin(115200); - delay(1000); // give me time to bring up serial monitor - - Serial.println("\n ESP32 Touch Interrupt Test\n"); - touchAttachInterrupt(T1, gotTouch1, threshold); - touchAttachInterrupt(T2, gotTouch2, threshold); -} - -void loop() { - if (touch1detected) { - touch1detected = false; - if (touchInterruptGetLastStatus(T1)) { - Serial.println(" --- T1 Touched"); - } else { - Serial.println(" --- T1 Released"); - } - } - if (touch2detected) { - touch2detected = false; - if (touchInterruptGetLastStatus(T2)) { - Serial.println(" --- T2 Touched"); - } else { - Serial.println(" --- T2 Released"); - } - } - - delay(80); -} diff --git a/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json b/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json deleted file mode 100644 index 2710fa7940e..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_VERSION_2=y" - ] -} diff --git a/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino b/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino index 3b4e5f0b9e9..130026d1ad7 100644 --- a/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino +++ b/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino @@ -3,11 +3,7 @@ This is an example how to use Touch Intrrerupts The bigger the threshold, the more sensible is the touch */ -#if CONFIG_IDF_TARGET_ESP32P4 -int threshold = 0; // when 0 is used, the benchmarked value will be used -#else -int threshold = 40; -#endif +int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) bool touch1detected = false; bool touch2detected = false; @@ -23,6 +19,10 @@ void gotTouch2() { void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor + + //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. + touchSetDefaultThreshold(5); + Serial.println("ESP32 Touch Interrupt Test"); touchAttachInterrupt(T2, gotTouch1, threshold); touchAttachInterrupt(T3, gotTouch2, threshold); diff --git a/libraries/ESP32/examples/Touch/TouchInterrupt/ci.json b/libraries/ESP32/examples/Touch/TouchInterrupt/ci.json deleted file mode 100644 index c0ecf9fc0a5..00000000000 --- a/libraries/ESP32/examples/Touch/TouchInterrupt/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y" - ] -} diff --git a/libraries/ESP32/examples/Touch/TouchInterrupt/ci.yml b/libraries/ESP32/examples/Touch/TouchInterrupt/ci.yml new file mode 100644 index 00000000000..feaef91cf65 --- /dev/null +++ b/libraries/ESP32/examples/Touch/TouchInterrupt/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y diff --git a/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino b/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino index 8e93ba44691..ba221f3048f 100644 --- a/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino +++ b/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino @@ -1,5 +1,5 @@ // ESP32 Touch Test -// Just test touch pin - Touch0 is T0 which is on GPIO 4. +// Just test touch pin - Touch2 is T2 which is on GPIO 2. void setup() { Serial.begin(115200); @@ -8,6 +8,6 @@ void setup() { } void loop() { - Serial.println(touchRead(T1)); // get value using T0 + Serial.println(touchRead(T2)); // get value using T2 delay(1000); } diff --git a/libraries/ESP32/examples/Touch/TouchRead/ci.json b/libraries/ESP32/examples/Touch/TouchRead/ci.json deleted file mode 100644 index c0ecf9fc0a5..00000000000 --- a/libraries/ESP32/examples/Touch/TouchRead/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y" - ] -} diff --git a/libraries/ESP32/examples/Touch/TouchRead/ci.yml b/libraries/ESP32/examples/Touch/TouchRead/ci.yml new file mode 100644 index 00000000000..feaef91cf65 --- /dev/null +++ b/libraries/ESP32/examples/Touch/TouchRead/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y diff --git a/libraries/ESP32/library.properties b/libraries/ESP32/library.properties index e664022388d..ef14a246302 100644 --- a/libraries/ESP32/library.properties +++ b/libraries/ESP32/library.properties @@ -1,5 +1,5 @@ name=ESP32 -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 sketches examples diff --git a/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino new file mode 100644 index 00000000000..01c40d5cebd --- /dev/null +++ b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ESP_HostedOTA.ino @@ -0,0 +1,66 @@ +/* + * ESP-Hosted OTA Update Example + * + * This example demonstrates how to update the ESP-Hosted co-processor firmware + * over-the-air (OTA). The ESP-Hosted solution allows an ESP32 to act as a WiFi + * co-processor for other microcontrollers. + * + * Prerequisites: + * - ESP32 with ESP-Hosted firmware configured as WiFi co-processor + * - Network connectivity to download firmware updates + * - Valid WiFi credentials + */ + +#include "WiFi.h" // WiFi library for network connectivity +#include "ESP_HostedOTA.h" // ESP-Hosted OTA update functionality + +// WiFi network credentials - CHANGE THESE TO YOUR NETWORK SETTINGS +const char *ssid = "your-ssid"; // Replace with your WiFi network name +const char *password = "your-password"; // Replace with your WiFi password + +void setup() { + // Step 1: Initialize serial communication for debugging output + Serial.begin(115200); + + // Step 2: Initialize the ESP-Hosted WiFi station mode + // This prepares the ESP-Hosted co-processor for WiFi operations + WiFi.STA.begin(); + + // Step 3: Display connection attempt information + Serial.println(); + Serial.println("******************************************************"); + Serial.print("Connecting to "); + Serial.println(ssid); + + // Step 4: Attempt to connect to the specified WiFi network + WiFi.STA.connect(ssid, password); + + // Step 5: Wait for WiFi connection to be established + // Display progress dots while connecting + while (WiFi.STA.status() != WL_CONNECTED) { + delay(500); // Wait 500ms between connection attempts + Serial.print("."); // Show connection progress + } + Serial.println(); + + // Step 6: Display successful connection information + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.STA.localIP()); + + // Step 7: Attempt to update the ESP-Hosted co-processor firmware + // This function will: + // - Check if ESP-Hosted is initialized + // - Verify if an update is available + // - Download and install the firmware update if needed + if (updateEspHostedSlave()) { + // Step 8: Restart the host ESP32 after successful update + // This is currently required to properly activate the new firmware + // on the ESP-Hosted co-processor + ESP.restart(); + } +} + +void loop() { + delay(1000); +} diff --git a/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml new file mode 100644 index 00000000000..8548a039255 --- /dev/null +++ b/libraries/ESP_HostedOTA/examples/ESP_HostedOTA/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ESP_HostedOTA/keywords.txt b/libraries/ESP_HostedOTA/keywords.txt new file mode 100644 index 00000000000..f34de27a296 --- /dev/null +++ b/libraries/ESP_HostedOTA/keywords.txt @@ -0,0 +1,17 @@ +####################################### +# Syntax Coloring Map For ESP_HostedOTA +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +updateEspHostedSlave KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/libraries/ESP_HostedOTA/library.properties b/libraries/ESP_HostedOTA/library.properties new file mode 100644 index 00000000000..a45ab7aa138 --- /dev/null +++ b/libraries/ESP_HostedOTA/library.properties @@ -0,0 +1,9 @@ +name=ESP_HostedOTA +version=3.3.4 +author=me-no-dev +maintainer=me-no-dev +sentence=Library for updating the ESP-Hosted co-processor +paragraph=Supports ESP32 Arduino platforms. +category=Communication +url=https://github.com/espressif/arduino-esp32/ +architectures=esp32 diff --git a/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp new file mode 100644 index 00000000000..6ba2f290aa8 --- /dev/null +++ b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.cpp @@ -0,0 +1,163 @@ +// Include necessary headers for SDK configuration, Arduino framework, and networking +#include "sdkconfig.h" +#include +#if CONFIG_ESP_WIFI_REMOTE_ENABLED +#include "Arduino.h" +#include "esp32-hal-hosted.h" // ESP-Hosted specific functions +#include "Network.h" // Network connectivity management +#include "HTTPClient.h" // HTTP client for downloading updates +#include "NetworkClientSecure.h" // Secure network client for HTTPS +#endif + +/** + * Updates the ESP-Hosted co-processor firmware over-the-air (OTA) + * This function downloads and installs firmware updates for the ESP-Hosted slave device + * @return true if update was successful, false otherwise + */ +bool updateEspHostedSlave() { +#if CONFIG_ESP_WIFI_REMOTE_ENABLED + bool updateSuccess = false; + + // Step 1: Verify ESP-Hosted is properly initialized + if (!hostedIsInitialized()) { + Serial.println("ERROR: esp-hosted is not initialized. Did you call WiFi.STA.begin()?"); + return updateSuccess; + } + + // Step 2: Check if an update is actually available + if (!hostedHasUpdate()) { + // esp-hosted is already the latest version - no update needed + return updateSuccess; + } + + // Step 3: Ensure network connectivity is available + if (!Network.isOnline()) { + Serial.println("ERROR: Network is not online! Did you call WiFi.STA.connect(ssid, password)?"); + return updateSuccess; + } + + // Step 4: Begin the update process - display update URL + Serial.print("Updating esp-hosted co-processor from "); + Serial.println(hostedGetUpdateURL()); + + // Step 5: Create a secure network client for HTTPS communication + NetworkClientSecure *client = new NetworkClientSecure(); + if (!client) { + Serial.println("ERROR: Could not allocate client!"); + return updateSuccess; + } + + // Step 6: Configure client to skip certificate verification (insecure mode) + client->setInsecure(); + + // Step 7: Initialize HTTP client and attempt to connect to update server + HTTPClient https; + int httpCode = 0; + if (!https.begin(*client, hostedGetUpdateURL())) { + Serial.println("ERROR: HTTP begin failed!"); + goto finish_ota; + } + + // Step 8: Send HTTP GET request to download the firmware + httpCode = https.GET(); + if (httpCode == HTTP_CODE_OK) { + // Step 9: Get the size of the firmware file to download + int len = https.getSize(); + if (len < 0) { + Serial.println("ERROR: Update size not received!"); + https.end(); + goto finish_ota; + } + + // Step 10: Get stream pointer for reading firmware data + NetworkClient *stream = https.getStreamPtr(); + + // Step 11: Initialize the ESP-Hosted update process + if (!hostedBeginUpdate()) { + Serial.println("ERROR: esp-hosted update start failed!"); + https.end(); + goto finish_ota; + } + +// Step 12: Allocate buffer for firmware data transfer (2KB chunks) +#define HOSTED_OTA_BUF_SIZE 2048 + uint8_t *buff = (uint8_t *)malloc(HOSTED_OTA_BUF_SIZE); + if (!buff) { + Serial.println("ERROR: Could not allocate OTA buffer!"); + https.end(); + goto finish_ota; + } + + // Step 13: Download and write firmware data in chunks + while (https.connected() && len > 0) { + size_t size = stream->available(); + if (size > 0) { + // Show progress indicator + Serial.print("."); + + // Limit chunk size to buffer capacity + if (size > HOSTED_OTA_BUF_SIZE) { + size = HOSTED_OTA_BUF_SIZE; + } + + // Prevent reading more data than expected + if (size > len) { + Serial.printf("\nERROR: Update received extra bytes: %u!", size - len); + break; + } + + // Read firmware data chunk into buffer + int readLen = stream->readBytes(buff, size); + len -= readLen; + + // Write the chunk to ESP-Hosted co-processor + if (!hostedWriteUpdate(buff, readLen)) { + Serial.println("\nERROR: esp-hosted update write failed!"); + break; + } + + // Step 14: Check if entire firmware has been downloaded + if (len == 0) { + // Finalize the update process + if (!hostedEndUpdate()) { + Serial.println("\nERROR: esp-hosted update end failed!"); + break; + } + + // Activate the new firmware + if (!hostedActivateUpdate()) { + Serial.println("\nERROR: esp-hosted update activate failed!"); + break; + } + + // Update completed successfully + updateSuccess = true; + Serial.println("\nSUCCESS: esp-hosted co-processor updated!"); + break; + } + } + // Small delay to prevent overwhelming the system + delay(1); + } + + // Step 15: Clean up allocated buffer + free(buff); + Serial.println(); + } else if (httpCode == HTTP_CODE_NOT_FOUND) { + Serial.println("ERROR: Update file not found!"); + } else { + Serial.printf("ERROR: HTTP request failed with code %d!", httpCode); + } + + // Step 16: Close HTTP connection + https.end(); + +finish_ota: + // Step 17: Clean up network client + delete client; + return updateSuccess; +#else + // ESP-Hosted functionality is not enabled in SDK configuration + return false; +#endif +} diff --git a/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h new file mode 100644 index 00000000000..56c35ec2b07 --- /dev/null +++ b/libraries/ESP_HostedOTA/src/ESP_HostedOTA.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +/** + * Updates the ESP-Hosted co-processor firmware over-the-air (OTA) + * This function downloads and installs firmware updates for the ESP-Hosted slave device + * @return true if update was successful, false otherwise + */ +bool updateEspHostedSlave(); diff --git a/libraries/ESP_I2S/examples/ES8388_loopback/ci.json b/libraries/ESP_I2S/examples/ES8388_loopback/ci.json deleted file mode 100644 index e0f64e28943..00000000000 --- a/libraries/ESP_I2S/examples/ES8388_loopback/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2S_SUPPORTED=y", - "CONFIG_SOC_I2C_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_I2S/examples/ES8388_loopback/ci.yml b/libraries/ESP_I2S/examples/ES8388_loopback/ci.yml new file mode 100644 index 00000000000..1deb186296b --- /dev/null +++ b/libraries/ESP_I2S/examples/ES8388_loopback/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_SOC_I2S_SUPPORTED=y + - CONFIG_SOC_I2C_SUPPORTED=y diff --git a/libraries/ESP_I2S/examples/Record_to_WAV/ci.json b/libraries/ESP_I2S/examples/Record_to_WAV/ci.json deleted file mode 100644 index a45dc2f0120..00000000000 --- a/libraries/ESP_I2S/examples/Record_to_WAV/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_SDMMC_HOST_SUPPORTED=y", - "CONFIG_SOC_I2S_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_I2S/examples/Record_to_WAV/ci.yml b/libraries/ESP_I2S/examples/Record_to_WAV/ci.yml new file mode 100644 index 00000000000..7ab2c964b4b --- /dev/null +++ b/libraries/ESP_I2S/examples/Record_to_WAV/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_SOC_SDMMC_HOST_SUPPORTED=y + - CONFIG_SOC_I2S_SUPPORTED=y diff --git a/libraries/ESP_I2S/examples/Simple_tone/ci.json b/libraries/ESP_I2S/examples/Simple_tone/ci.json deleted file mode 100644 index 9842f2f9b2a..00000000000 --- a/libraries/ESP_I2S/examples/Simple_tone/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2S_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_I2S/examples/Simple_tone/ci.yml b/libraries/ESP_I2S/examples/Simple_tone/ci.yml new file mode 100644 index 00000000000..5107c290753 --- /dev/null +++ b/libraries/ESP_I2S/examples/Simple_tone/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_I2S_SUPPORTED=y diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties index b2763f4e7e8..da985780ca7 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -1,9 +1,9 @@ name=ESP_I2S -version=3.3.0 +version=3.3.4 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication paragraph=Supports ESP32 Arduino platforms. -category=Sound +category=Signal Input/Output url=https://github.com/espressif/arduino-esp32/ architectures=esp32 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.json b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.yml b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Master/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.json b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.yml b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Broadcast_Slave/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino index d30d6cd40cf..ec61cb57cf9 100644 --- a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino +++ b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino @@ -32,6 +32,7 @@ #include // For the MAC2STR and MACSTR macros #include +#include //std::nothrow /* Definitions */ @@ -235,7 +236,7 @@ void register_new_peer(const esp_now_recv_info_t *info, const uint8_t *data, int if (current_peer_count < ESPNOW_PEER_COUNT) { Serial.printf("New peer found: " MACSTR " with priority %d\n", MAC2STR(info->src_addr), priority); - ESP_NOW_Network_Peer *new_peer = new ESP_NOW_Network_Peer(info->src_addr, priority); + ESP_NOW_Network_Peer *new_peer = new (std::nothrow) ESP_NOW_Network_Peer(info->src_addr, priority); if (new_peer == nullptr || !new_peer->begin()) { Serial.println("Failed to create or register the new peer"); delete new_peer; diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.json b/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.yml b/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Network/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.json b/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.yml b/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/ESP_NOW/examples/ESP_NOW_Serial/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties index f8e627dbc03..cae0ed03dc3 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -1,9 +1,9 @@ name=ESP_NOW -version=3.3.0 +version=3.3.4 author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW paragraph=Supports ESP32 Arduino platforms. -category=Sensor +category=Communication url=https://github.com/espressif/arduino-esp32/ architectures=esp32 diff --git a/libraries/ESP_NOW/src/ESP32_NOW.cpp b/libraries/ESP_NOW/src/ESP32_NOW.cpp index e61160a16dd..3fb96277dbb 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW.cpp +++ b/libraries/ESP_NOW/src/ESP32_NOW.cpp @@ -18,8 +18,10 @@ static void *new_arg = nullptr; // * tx_arg = nullptr, * rx_arg = nullptr, static bool _esp_now_has_begun = false; static ESP_NOW_Peer *_esp_now_peers[ESP_NOW_MAX_TOTAL_PEER_NUM]; -static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, ESP_NOW_Peer *_peer = nullptr) { - log_v(MACSTR, MAC2STR(mac_addr)); +static esp_err_t _esp_now_add_peer( + const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, esp_now_rate_config_t *rate_config, ESP_NOW_Peer *_peer = nullptr +) { + log_i("Adding peer " MACSTR, MAC2STR(mac_addr)); if (esp_now_is_peer_exist(mac_addr)) { log_e("Peer Already Exists"); return ESP_ERR_ESPNOW_EXIST; @@ -41,9 +43,17 @@ static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wif for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { if (_esp_now_peers[i] == nullptr) { _esp_now_peers[i] = _peer; + if (_esp_now_has_begun && rate_config != nullptr) { + log_i("ESP-NOW already running. Setting PHY rate for peer " MACSTR, MAC2STR(_peer->addr())); + result = esp_now_set_peer_rate_config(_peer->addr(), rate_config); + if (result != ESP_OK) { + log_w("Could not set the ESP-NOW PHY rate for peer " MACSTR, MAC2STR(_peer->addr())); + } + } return ESP_OK; } } + log_e("Library Peer list full"); return ESP_FAIL; } } else if (result == ESP_ERR_ESPNOW_NOT_INIT) { @@ -51,7 +61,7 @@ static esp_err_t _esp_now_add_peer(const uint8_t *mac_addr, uint8_t channel, wif } else if (result == ESP_ERR_ESPNOW_ARG) { log_e("Invalid Argument"); } else if (result == ESP_ERR_ESPNOW_FULL) { - log_e("Peer list full"); + log_e("ESP-NOW Peer list full"); } else if (result == ESP_ERR_ESPNOW_NO_MEM) { log_e("Out of memory"); } else if (result == ESP_ERR_ESPNOW_EXIST) { @@ -149,6 +159,21 @@ static void _esp_now_tx_cb(const uint8_t *mac_addr, esp_now_send_status_t status } } +esp_err_t _esp_now_set_all_peers_rate() { + for (uint8_t i = 0; i < ESP_NOW_MAX_TOTAL_PEER_NUM; i++) { + if (_esp_now_peers[i] != nullptr) { + log_v("Setting PHY rate for peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr())); + esp_now_rate_config_t rate = _esp_now_peers[i]->getRate(); + esp_err_t err = esp_now_set_peer_rate_config(_esp_now_peers[i]->addr(), &rate); + if (err != ESP_OK) { + log_e("Failed to set rate for peer " MACSTR, MAC2STR(_esp_now_peers[i]->addr())); + return err; + } + } + } + return ESP_OK; +} + ESP_NOW_Class::ESP_NOW_Class() { max_data_len = 0; version = 0; @@ -195,6 +220,14 @@ bool ESP_NOW_Class::begin(const uint8_t *pmk) { return false; } + // Set the peers PHY rate after initializing ESP-NOW. + err = _esp_now_set_all_peers_rate(); + if (err != ESP_OK) { + log_e("Failed to set PHY rate for peers! 0x%x", err); + _esp_now_has_begun = false; + return false; + } + if (pmk) { err = esp_now_set_pmk(pmk); if (err != ESP_OK) { @@ -243,6 +276,7 @@ bool ESP_NOW_Class::end() { int ESP_NOW_Class::getTotalPeerCount() const { if (!_esp_now_has_begun) { + log_e("ESP-NOW not initialized"); return -1; } esp_now_peer_num_t num; @@ -256,6 +290,7 @@ int ESP_NOW_Class::getTotalPeerCount() const { int ESP_NOW_Class::getEncryptedPeerCount() const { if (!_esp_now_has_begun) { + log_e("ESP-NOW not initialized"); return -1; } esp_now_peer_num_t num; @@ -295,6 +330,7 @@ int ESP_NOW_Class::availableForWrite() { size_t ESP_NOW_Class::write(const uint8_t *data, size_t len) { if (!_esp_now_has_begun) { + log_e("ESP-NOW not initialized. Please call begin() first to send data."); return 0; } if (len > max_data_len) { @@ -336,7 +372,7 @@ ESP_NOW_Class ESP_NOW; * */ -ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk) { +ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interface_t iface, const uint8_t *lmk, esp_now_rate_config_t *rate_config) { added = false; if (mac_addr) { memcpy(mac, mac_addr, 6); @@ -347,6 +383,11 @@ ESP_NOW_Peer::ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel, wifi_interf if (encrypt) { memcpy(key, lmk, 16); } + if (rate_config) { + rate = *rate_config; + } else { + rate = DEFAULT_ESPNOW_RATE_CONFIG; + } } bool ESP_NOW_Peer::add() { @@ -356,7 +397,8 @@ bool ESP_NOW_Peer::add() { if (added) { return true; } - if (_esp_now_add_peer(mac, chan, ifc, encrypt ? key : nullptr, this) != ESP_OK) { + if (_esp_now_add_peer(mac, chan, ifc, encrypt ? key : nullptr, &rate, this) != ESP_OK) { + log_e("Failed to add peer " MACSTR, MAC2STR(mac)); return false; } log_v("Peer added - " MACSTR, MAC2STR(mac)); @@ -371,12 +413,15 @@ bool ESP_NOW_Peer::remove() { if (!added) { return true; } - log_v("Peer removed - " MACSTR, MAC2STR(mac)); + log_i("Removing peer - " MACSTR, MAC2STR(mac)); esp_err_t err = _esp_now_del_peer(mac); if (err == ESP_OK) { added = false; + log_i("Peer removed - " MACSTR, MAC2STR(mac)); return true; } + + log_e("Failed to remove peer " MACSTR, MAC2STR(mac)); return false; } @@ -389,6 +434,8 @@ bool ESP_NOW_Peer::addr(const uint8_t *mac_addr) { memcpy(mac, mac_addr, 6); return true; } + log_e("Peer already added and ESP-NOW is already running. Cannot change the MAC address."); + log_e("Please call addr() before adding the peer or before starting ESP-NOW."); return false; } @@ -416,6 +463,38 @@ bool ESP_NOW_Peer::setInterface(wifi_interface_t iface) { return _esp_now_modify_peer(mac, chan, ifc, encrypt ? key : nullptr) == ESP_OK; } +/** + * @brief Set the rate configuration for the peer. + * + * @param rate_config Pointer to the rate configuration to set. Nullptr to reset to default rate configuration. + * @return true if the rate configuration was set successfully, false otherwise. + */ +bool ESP_NOW_Peer::setRate(const esp_now_rate_config_t *rate_config) { + if (added && _esp_now_has_begun) { + log_e("Peer already added and ESP-NOW is already running. Cannot set rate configuration."); + log_e("Please call setRate() before adding the peer or before starting ESP-NOW."); + return false; + } + + if (rate_config == nullptr) { + log_i("Resetting rate configuration to default."); + rate = DEFAULT_ESPNOW_RATE_CONFIG; + } else { + rate = *rate_config; + } + + return true; +} + +/** + * @brief Get the rate configuration for the peer. + * + * @return esp_now_rate_config_t The rate configuration for the peer. + */ +esp_now_rate_config_t ESP_NOW_Peer::getRate() const { + return rate; +} + bool ESP_NOW_Peer::isEncrypted() const { return encrypt; } diff --git a/libraries/ESP_NOW/src/ESP32_NOW.h b/libraries/ESP_NOW/src/ESP32_NOW.h index 5b5bbe72673..42a24aa84d9 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW.h +++ b/libraries/ESP_NOW/src/ESP32_NOW.h @@ -11,6 +11,15 @@ #include "esp32-hal-log.h" #include "esp_mac.h" +// clang-format off +#define DEFAULT_ESPNOW_RATE_CONFIG { \ + .phymode = WIFI_PHY_MODE_11G, \ + .rate = WIFI_PHY_RATE_1M_L, \ + .ersu = false, \ + .dcm = false \ +} +// clang-format on + class ESP_NOW_Peer; //forward declaration for friend function class ESP_NOW_Class : public Print { @@ -29,6 +38,8 @@ class ESP_NOW_Class : public Print { int getVersion() const; int availableForWrite(); + + // You can directly send data to all peers without broadcasting using ESP_NOW.write(data, len) size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data) { return write(&data, 1); @@ -47,6 +58,7 @@ class ESP_NOW_Peer { uint8_t mac[6]; uint8_t chan; wifi_interface_t ifc; + esp_now_rate_config_t rate; bool encrypt; uint8_t key[16]; @@ -56,7 +68,10 @@ class ESP_NOW_Peer { bool remove(); size_t send(const uint8_t *data, int len); - ESP_NOW_Peer(const uint8_t *mac_addr, uint8_t channel = 0, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = nullptr); + ESP_NOW_Peer( + const uint8_t *mac_addr, uint8_t channel = 0, wifi_interface_t iface = WIFI_IF_AP, const uint8_t *lmk = nullptr, + esp_now_rate_config_t *rate_config = nullptr + ); public: virtual ~ESP_NOW_Peer() {} @@ -70,6 +85,9 @@ class ESP_NOW_Peer { wifi_interface_t getInterface() const; bool setInterface(wifi_interface_t iface); + bool setRate(const esp_now_rate_config_t *rate_config); + esp_now_rate_config_t getRate() const; + bool isEncrypted() const; bool setKey(const uint8_t *lmk); @@ -87,6 +105,8 @@ class ESP_NOW_Peer { friend bool ESP_NOW_Class::removePeer(ESP_NOW_Peer &); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ESP_NOW) extern ESP_NOW_Class ESP_NOW; +#endif #endif diff --git a/libraries/ESP_SR/examples/Basic/Basic.ino b/libraries/ESP_SR/examples/Basic/Basic.ino index 30aab69d79b..c8a16b1bc92 100644 --- a/libraries/ESP_SR/examples/Basic/Basic.ino +++ b/libraries/ESP_SR/examples/Basic/Basic.ino @@ -9,6 +9,17 @@ #define LIGHT_PIN 40 #define FAN_PIN 41 +/** + * The input format: + * M to represent the microphone channel + * R to represent the playback reference channel + * N to represent an unknown or unused channel + * + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ +#define SR_INPUT_FORMAT "MM" + I2SClass i2s; // Generated using the following command: @@ -69,7 +80,7 @@ void setup() { i2s.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); ESP_SR.onEvent(onSrEvent); - ESP_SR.begin(i2s, sr_commands, sizeof(sr_commands) / sizeof(sr_cmd_t), SR_CHANNELS_STEREO, SR_MODE_WAKEWORD); + ESP_SR.begin(i2s, sr_commands, sizeof(sr_commands) / sizeof(sr_cmd_t), SR_CHANNELS_STEREO, SR_MODE_WAKEWORD, SR_INPUT_FORMAT); } void loop() {} diff --git a/libraries/ESP_SR/examples/Basic/ci.json b/libraries/ESP_SR/examples/Basic/ci.json deleted file mode 100644 index ec0e969a7d0..00000000000 --- a/libraries/ESP_SR/examples/Basic/ci.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "fqbn": { - "esp32s3": [ - "espressif:esp32:esp32s3:USBMode=default,PartitionScheme=esp_sr_16,FlashSize=16M,FlashMode=dio" - ] - }, - "requires": [ - "CONFIG_SOC_I2S_SUPPORTED=y" - ], - "targets": { - "esp32": false, - "esp32c3": false, - "esp32c6": false, - "esp32h2": false, - "esp32p4": false, - "esp32s2": false, - "esp32c5": false - } -} diff --git a/libraries/ESP_SR/examples/Basic/ci.yml b/libraries/ESP_SR/examples/Basic/ci.yml new file mode 100644 index 00000000000..cdd66d7ce39 --- /dev/null +++ b/libraries/ESP_SR/examples/Basic/ci.yml @@ -0,0 +1,16 @@ +fqbn: + esp32s3: + - espressif:esp32:esp32s3:USBMode=default,PartitionScheme=esp_sr_16,FlashSize=16M,FlashMode=dio + esp32p4: + - espressif:esp32:esp32p4:USBMode=default,PartitionScheme=esp_sr_16,FlashSize=16M,FlashMode=qio + +requires: + - CONFIG_SOC_I2S_SUPPORTED=y + +targets: + esp32: false + esp32c3: false + esp32c6: false + esp32h2: false + esp32s2: false + esp32c5: false diff --git a/libraries/ESP_SR/library.properties b/libraries/ESP_SR/library.properties index 9d9787b7931..5e019e501a2 100644 --- a/libraries/ESP_SR/library.properties +++ b/libraries/ESP_SR/library.properties @@ -1,5 +1,5 @@ name=ESP_SR -version=3.3.0 +version=3.3.4 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP Sound Recognition diff --git a/libraries/ESP_SR/src/ESP_SR.cpp b/libraries/ESP_SR/src/ESP_SR.cpp index a149c1490e1..31cf85efd37 100644 --- a/libraries/ESP_SR/src/ESP_SR.cpp +++ b/libraries/ESP_SR/src/ESP_SR.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "ESP_SR.h" static esp_err_t on_sr_fill(void *arg, void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms) { @@ -25,9 +25,9 @@ void ESP_SR_Class::onEvent(sr_cb event_cb) { cb = event_cb; } -bool ESP_SR_Class::begin(I2SClass &_i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan, sr_mode_t mode) { +bool ESP_SR_Class::begin(I2SClass &_i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format) { i2s = &_i2s; - esp_err_t err = sr_start(on_sr_fill, this, rx_chan, mode, sr_commands, sr_commands_len, on_sr_event, this); + esp_err_t err = sr_start(on_sr_fill, this, rx_chan, mode, input_format, sr_commands, sr_commands_len, on_sr_event, this); return (err == ESP_OK); } diff --git a/libraries/ESP_SR/src/ESP_SR.h b/libraries/ESP_SR/src/ESP_SR.h index d637f686f42..05393504cb9 100644 --- a/libraries/ESP_SR/src/ESP_SR.h +++ b/libraries/ESP_SR/src/ESP_SR.h @@ -6,7 +6,7 @@ #pragma once #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "ESP_I2S.h" #include "esp32-hal-sr.h" @@ -23,7 +23,19 @@ class ESP_SR_Class { ~ESP_SR_Class(); void onEvent(sr_cb cb); - bool begin(I2SClass &i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan = SR_CHANNELS_STEREO, sr_mode_t mode = SR_MODE_WAKEWORD); + /** + * The input format: + * M to represent the microphone channel + * R to represent the playback reference channel + * N to represent an unknown or unused channel + * + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ + bool begin( + I2SClass &i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan = SR_CHANNELS_STEREO, sr_mode_t mode = SR_MODE_WAKEWORD, + const char *input_format = "MN" + ); bool end(void); bool setMode(sr_mode_t mode); bool pause(void); @@ -33,6 +45,8 @@ class ESP_SR_Class { esp_err_t _fill(void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ESP_SR) extern ESP_SR_Class ESP_SR; +#endif #endif // CONFIG_IDF_TARGET_ESP32S3 diff --git a/libraries/ESP_SR/src/esp32-hal-sr.c b/libraries/ESP_SR/src/esp32-hal-sr.c index eb87ef636c1..68cfb6a7a01 100644 --- a/libraries/ESP_SR/src/esp32-hal-sr.c +++ b/libraries/ESP_SR/src/esp32-hal-sr.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #if !defined(ARDUINO_PARTITION_esp_sr_32) && !defined(ARDUINO_PARTITION_esp_sr_16) && !defined(ARDUINO_PARTITION_esp_sr_8) #warning Compatible partition must be selected for ESP_SR to work @@ -313,7 +313,8 @@ esp_err_t sr_set_mode(sr_mode_t mode) { } esp_err_t sr_start( - sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t sr_commands[], size_t cmd_number, sr_event_cb cb, void *cb_arg + sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format, const sr_cmd_t sr_commands[], size_t cmd_number, + sr_event_cb cb, void *cb_arg ) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(NULL == g_sr_data, ESP_ERR_INVALID_STATE, "SR already running"); @@ -340,12 +341,11 @@ esp_err_t sr_start( models = esp_srmodel_init("model"); // Load WakeWord Detection - g_sr_data->afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE; - afe_config_t afe_config = AFE_CONFIG_DEFAULT(); - afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, "hiesp"); - afe_config.aec_init = false; - log_d("load wakenet '%s'", afe_config.wakenet_model_name); - g_sr_data->afe_data = g_sr_data->afe_handle->create_from_config(&afe_config); + afe_config_t *afe_config = afe_config_init(input_format, models, AFE_TYPE_SR, AFE_MODE_LOW_COST); + g_sr_data->afe_handle = esp_afe_handle_from_config(afe_config); + log_d("load wakenet '%s'", afe_config->wakenet_model_name); + g_sr_data->afe_data = g_sr_data->afe_handle->create_from_config(afe_config); + afe_config_free(afe_config); // Load Custom Command Detection char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_ENGLISH); diff --git a/libraries/ESP_SR/src/esp32-hal-sr.h b/libraries/ESP_SR/src/esp32-hal-sr.h index 894390634e0..ac3df3f07ac 100644 --- a/libraries/ESP_SR/src/esp32-hal-sr.h +++ b/libraries/ESP_SR/src/esp32-hal-sr.h @@ -6,7 +6,7 @@ #pragma once #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "driver/i2s_types.h" #include "esp_err.h" @@ -49,7 +49,8 @@ typedef void (*sr_event_cb)(void *arg, sr_event_t event, int command_id, int phr typedef esp_err_t (*sr_fill_cb)(void *arg, void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms); esp_err_t sr_start( - sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t *sr_commands, size_t cmd_number, sr_event_cb cb, void *cb_arg + sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format, const sr_cmd_t *sr_commands, size_t cmd_number, + sr_event_cb cb, void *cb_arg ); esp_err_t sr_stop(void); esp_err_t sr_pause(void); diff --git a/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.json b/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.yml b/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/ESPmDNS/examples/mDNS-SD_Extended/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.json b/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.yml b/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/ESPmDNS/examples/mDNS_Web_Server/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ESPmDNS/library.properties b/libraries/ESPmDNS/library.properties index 062d3b90b51..44a14541f40 100644 --- a/libraries/ESPmDNS/library.properties +++ b/libraries/ESPmDNS/library.properties @@ -1,5 +1,5 @@ name=ESPmDNS -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 mDNS Library diff --git a/libraries/Ethernet/examples/ETH_LAN8720/ci.json b/libraries/Ethernet/examples/ETH_LAN8720/ci.json deleted file mode 100644 index 0eab13b8841..00000000000 --- a/libraries/Ethernet/examples/ETH_LAN8720/ci.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "requires": [ - "CONFIG_ETH_USE_ESP32_EMAC=y" - ], - "targets": { - "esp32p4": false - } -} diff --git a/libraries/Ethernet/examples/ETH_LAN8720/ci.yml b/libraries/Ethernet/examples/ETH_LAN8720/ci.yml new file mode 100644 index 00000000000..87dccc48269 --- /dev/null +++ b/libraries/Ethernet/examples/ETH_LAN8720/ci.yml @@ -0,0 +1,5 @@ +requires: + - CONFIG_ETH_USE_ESP32_EMAC=y + +targets: + esp32p4: false diff --git a/libraries/Ethernet/examples/ETH_TLK110/ci.json b/libraries/Ethernet/examples/ETH_TLK110/ci.json deleted file mode 100644 index dcdfd06db51..00000000000 --- a/libraries/Ethernet/examples/ETH_TLK110/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_ETH_USE_ESP32_EMAC=y" - ] -} diff --git a/libraries/Ethernet/examples/ETH_TLK110/ci.yml b/libraries/Ethernet/examples/ETH_TLK110/ci.yml new file mode 100644 index 00000000000..1c051776e19 --- /dev/null +++ b/libraries/Ethernet/examples/ETH_TLK110/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_ETH_USE_ESP32_EMAC=y diff --git a/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.json b/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.yml b/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Ethernet/examples/ETH_WIFI_BRIDGE/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Ethernet/library.properties b/libraries/Ethernet/library.properties index 28f2a8697d9..bc342dff285 100644 --- a/libraries/Ethernet/library.properties +++ b/libraries/Ethernet/library.properties @@ -1,5 +1,5 @@ name=Ethernet -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 Ethernet. diff --git a/libraries/Ethernet/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp index 3dfba37c684..8d1e459030c 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -34,6 +34,9 @@ #if defined __has_include && __has_include("soc/emac_ext_struct.h") #include "soc/emac_ext_struct.h" #endif /* __has_include("soc/emac_ext_struct.h" */ +#if ETH_PHY_LAN867X_SUPPORTED +#include "esp_eth_phy_lan867x.h" +#endif #include "soc/rtc.h" #endif /* CONFIG_ETH_USE_ESP32_EMAC */ #include "esp32-hal-periman.h" @@ -50,7 +53,7 @@ static ETHClass *_ethernets[NUM_SUPPORTED_ETH_PORTS] = {NULL, NULL, NULL}; static esp_event_handler_instance_t _eth_ev_instance = NULL; static void _eth_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { - + (void)arg; if (event_base == ETH_EVENT) { esp_eth_handle_t eth_handle = *((esp_eth_handle_t *)event_data); for (int i = 0; i < NUM_SUPPORTED_ETH_PORTS; ++i) { @@ -104,14 +107,17 @@ void ETHClass::_onEthEvent(int32_t event_id, void *event_data) { } else if (event_id == ETHERNET_EVENT_DISCONNECTED) { log_v("%s Disconnected", desc()); arduino_event.event_id = ARDUINO_EVENT_ETH_DISCONNECTED; + arduino_event.event_info.eth_disconnected = handle(); clearStatusBits(ESP_NETIF_CONNECTED_BIT | ESP_NETIF_HAS_IP_BIT | ESP_NETIF_HAS_LOCAL_IP6_BIT | ESP_NETIF_HAS_GLOBAL_IP6_BIT); } else if (event_id == ETHERNET_EVENT_START) { log_v("%s Started", desc()); arduino_event.event_id = ARDUINO_EVENT_ETH_START; + arduino_event.event_info.eth_started = handle(); setStatusBits(ESP_NETIF_STARTED_BIT); } else if (event_id == ETHERNET_EVENT_STOP) { log_v("%s Stopped", desc()); arduino_event.event_id = ARDUINO_EVENT_ETH_STOP; + arduino_event.event_info.eth_stopped = handle(); clearStatusBits( ESP_NETIF_STARTED_BIT | ESP_NETIF_CONNECTED_BIT | ESP_NETIF_HAS_IP_BIT | ESP_NETIF_HAS_LOCAL_IP6_BIT | ESP_NETIF_HAS_GLOBAL_IP6_BIT | ESP_NETIF_HAS_STATIC_IP_BIT @@ -143,6 +149,9 @@ ETHClass::ETHClass(uint8_t eth_index) ETHClass::~ETHClass() {} bool ETHClass::ethDetachBus(void *bus_pointer) { + if (!bus_pointer) { + return true; + } ETHClass *bus = (ETHClass *)bus_pointer; bus->end(); return true; @@ -292,10 +301,14 @@ bool ETHClass::begin(eth_phy_type_t type, int32_t phy_addr, int mdc, int mdio, i case ETH_PHY_DP83848: _phy = esp_eth_phy_new_dp83848(&phy_config); break; case ETH_PHY_KSZ8041: _phy = esp_eth_phy_new_ksz80xx(&phy_config); break; case ETH_PHY_KSZ8081: _phy = esp_eth_phy_new_ksz80xx(&phy_config); break; - default: log_e("Unsupported PHY %d", type); break; +#if ETH_PHY_LAN867X_SUPPORTED + case ETH_PHY_LAN867X: _phy = esp_eth_phy_new_lan867x(&phy_config); break; +#endif + default: log_e("Unsupported PHY %d", type); break; } if (_phy == NULL) { log_e("esp_eth_phy_new failed"); + _delMacAndPhy(); return false; } @@ -729,15 +742,29 @@ bool ETHClass::beginSPI( return false; } + if (_mac == NULL) { + log_e("esp_eth_mac_new failed"); + _delMacAndPhy(); + return false; + } + + if (_phy == NULL) { + log_e("esp_eth_phy_new failed"); + _delMacAndPhy(); + return false; + } + // Init Ethernet driver to default and install it esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(_mac, _phy); ret = esp_eth_driver_install(ð_config, &_eth_handle); if (ret != ESP_OK) { log_e("SPI Ethernet driver install failed: %d", ret); + _delMacAndPhy(); return false; } if (_eth_handle == NULL) { log_e("esp_eth_driver_install failed! eth_handle is NULL"); + _delMacAndPhy(); return false; } @@ -747,9 +774,9 @@ bool ETHClass::beginSPI( } else { // Derive a new MAC address for this interface uint8_t base_mac_addr[ETH_ADDR_LEN]; - ret = esp_efuse_mac_get_default(base_mac_addr); + ret = esp_read_mac(base_mac_addr, ESP_MAC_ETH); if (ret != ESP_OK) { - log_e("Get EFUSE MAC failed: %d", ret); + log_e("Get ETH MAC failed: %d", ret); return false; } base_mac_addr[ETH_ADDR_LEN - 1] += _eth_index; //Increment by the ETH number @@ -914,6 +941,18 @@ static bool empty_ethDetachBus(void *bus_pointer) { return true; } +void ETHClass::_delMacAndPhy() { + if (_mac != NULL) { + _mac->del(_mac); + _mac = NULL; + } + + if (_phy != NULL) { + _phy->del(_phy); + _phy = NULL; + } +} + void ETHClass::end(void) { Network.removeEvent(_eth_connected_event_handle); @@ -945,18 +984,10 @@ void ETHClass::end(void) { return; } _eth_handle = NULL; - //delete mac - if (_mac != NULL) { - _mac->del(_mac); - _mac = NULL; - } - //delete phy - if (_phy != NULL) { - _phy->del(_phy); - _phy = NULL; - } } + _delMacAndPhy(); + if (_eth_ev_instance != NULL) { bool do_not_unreg_ev_handler = false; for (int i = 0; i < NUM_SUPPORTED_ETH_PORTS; ++i) { @@ -1165,6 +1196,8 @@ size_t ETHClass::printDriverInfo(Print &out) const { return bytes; } +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ETH) ETHClass ETH; +#endif #endif /* CONFIG_ETH_ENABLED */ diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h index c52aac6ec6f..ceeaf789848 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -78,6 +78,9 @@ #include "esp_netif.h" #if CONFIG_ETH_USE_ESP32_EMAC +#if defined __has_include && __has_include("esp_eth_phy_lan867x.h") +#define ETH_PHY_LAN867X_SUPPORTED 1 +#endif #define ETH_PHY_IP101 ETH_PHY_TLK110 #if CONFIG_IDF_TARGET_ESP32 typedef enum { @@ -138,6 +141,9 @@ typedef enum { ETH_PHY_DP83848, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, +#if ETH_PHY_LAN867X_SUPPORTED + ETH_PHY_LAN867X, +#endif #endif /* CONFIG_ETH_USE_ESP32_EMAC */ #if CONFIG_ETH_SPI_ETHERNET_DM9051 ETH_PHY_DM9051, @@ -265,10 +271,14 @@ class ETHClass : public NetworkInterface { bool _setLinkSpeed(uint16_t speed); bool _setAutoNegotiation(bool on); + void _delMacAndPhy(); + friend class EthernetClass; // to access beginSPI }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ETH) extern ETHClass ETH; +#endif #endif /* _ETH_H_ */ #endif /* CONFIG_ETH_ENABLED */ diff --git a/libraries/FFat/examples/FFat_time/FFat_time.ino b/libraries/FFat/examples/FFat_time/FFat_time.ino index 392647c9923..e379c5f4f4d 100644 --- a/libraries/FFat/examples/FFat_time/FFat_time.ino +++ b/libraries/FFat/examples/FFat_time/FFat_time.ino @@ -28,10 +28,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -42,10 +43,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/FFat/examples/FFat_time/ci.json b/libraries/FFat/examples/FFat_time/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/FFat/examples/FFat_time/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/FFat/examples/FFat_time/ci.yml b/libraries/FFat/examples/FFat_time/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/FFat/examples/FFat_time/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/FFat/library.properties b/libraries/FFat/library.properties index 25b8c4e8acd..707c593d9d2 100644 --- a/libraries/FFat/library.properties +++ b/libraries/FFat/library.properties @@ -1,5 +1,5 @@ name=FFat -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov, Larry Bernstone maintainer=Hristo Gochkov sentence=ESP32 FAT on Flash File System diff --git a/libraries/FFat/src/FFat.h b/libraries/FFat/src/FFat.h index 3f700396777..56929f2f259 100644 --- a/libraries/FFat/src/FFat.h +++ b/libraries/FFat/src/FFat.h @@ -39,6 +39,8 @@ class F_Fat : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_FFAT) extern fs::F_Fat FFat; +#endif #endif /* _FFAT_H_ */ diff --git a/libraries/FS/library.properties b/libraries/FS/library.properties index 0f05f1134d5..1ff59d3308d 100644 --- a/libraries/FS/library.properties +++ b/libraries/FS/library.properties @@ -1,5 +1,5 @@ name=FS -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 File System diff --git a/libraries/FS/src/vfs_api.cpp b/libraries/FS/src/vfs_api.cpp index 616f37ac611..9afb4999119 100644 --- a/libraries/FS/src/vfs_api.cpp +++ b/libraries/FS/src/vfs_api.cpp @@ -13,6 +13,9 @@ // limitations under the License. #include "vfs_api.h" +#include +#include +#include using namespace fs; @@ -38,67 +41,49 @@ FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create strcpy(temp, _mountpoint); strcat(temp, fpath); - struct stat st; - //file found - if (!stat(temp, &st)) { - free(temp); - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { - return std::make_shared(this, fpath, mode); - } - log_e("%s has wrong mode 0x%08X", fpath, st.st_mode); - return FileImplPtr(); - } - - //try to open this as directory (might be mount point) - DIR *d = opendir(temp); - if (d) { - closedir(d); - free(temp); - return std::make_shared(this, fpath, mode); - } - - //file not found but mode permits file creation without folder creation - if ((mode && mode[0] != 'r') && (!create)) { - free(temp); - return std::make_shared(this, fpath, mode); - } - - ////file not found but mode permits file creation and folder creation - if ((mode && mode[0] != 'r') && create) { - - char *token; - char *folder = (char *)malloc(strlen(fpath)); - - int start_index = 0; - int end_index = 0; - - token = strchr(fpath + 1, '/'); - end_index = (token - fpath); - - while (token != NULL) { - memcpy(folder, fpath + start_index, end_index - start_index); - folder[end_index - start_index] = '\0'; - - if (!VFSImpl::mkdir(folder)) { - log_e("Creating folder: %s failed!", folder); - return FileImplPtr(); + // Try to open as file first - let the file operation handle errors + if (mode && mode[0] != 'r') { + // For write modes, attempt to create directories if needed + if (create) { + char *token; + char *folder = (char *)malloc(strlen(fpath) + 1); + + int start_index = 0; + int end_index = 0; + + token = strchr(fpath + 1, '/'); + end_index = (token - fpath); + + while (token != NULL) { + memcpy(folder, fpath + start_index, end_index - start_index); + folder[end_index - start_index] = '\0'; + + if (!VFSImpl::mkdir(folder)) { + log_e("Creating folder: %s failed!", folder); + free(folder); + free(temp); + return FileImplPtr(); + } + + token = strchr(token + 1, '/'); + if (token != NULL) { + end_index = (token - fpath); + memset(folder, 0, strlen(folder)); + } } - token = strchr(token + 1, '/'); - if (token != NULL) { - end_index = (token - fpath); - memset(folder, 0, strlen(folder)); - } + free(folder); } - free(folder); + // Try to open the file directly - let fopen handle errors free(temp); return std::make_shared(this, fpath, mode); } - log_e("%s does not exist, no permits for creation", temp); + // For read mode, let the VFSFileImpl constructor handle the file opening + // This avoids the TOCTOU race condition while maintaining proper functionality free(temp); - return FileImplPtr(); + return std::make_shared(this, fpath, mode); } bool VFSImpl::exists(const char *fpath) { @@ -125,10 +110,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) { log_e("bad arguments"); return false; } - if (!exists(pathFrom)) { - log_e("%s does not exists", pathFrom); - return false; - } + size_t mountpointLen = strlen(_mountpoint); char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1); if (!temp1) { @@ -148,6 +130,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) { strcpy(temp2, _mountpoint); strcat(temp2, pathTo); + // Let rename() handle the error if source doesn't exist auto rc = ::rename(temp1, temp2); free(temp1); free(temp2); @@ -165,16 +148,6 @@ bool VFSImpl::remove(const char *fpath) { return false; } - VFSFileImpl f(this, fpath, "r"); - if (!f || f.isDirectory()) { - if (f) { - f.close(); - } - log_e("%s does not exists or is directory", fpath); - return false; - } - f.close(); - char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1); if (!temp) { log_e("malloc failed"); @@ -184,6 +157,7 @@ bool VFSImpl::remove(const char *fpath) { strcpy(temp, _mountpoint); strcat(temp, fpath); + // Let unlink() handle the error if file doesn't exist auto rc = unlink(temp); free(temp); return rc == 0; @@ -231,16 +205,6 @@ bool VFSImpl::rmdir(const char *fpath) { return false; } - VFSFileImpl f(this, fpath, "r"); - if (!f || !f.isDirectory()) { - if (f) { - f.close(); - } - log_e("%s does not exists or is a file", fpath); - return false; - } - f.close(); - char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1); if (!temp) { log_e("malloc failed"); @@ -250,6 +214,7 @@ bool VFSImpl::rmdir(const char *fpath) { strcpy(temp, _mountpoint); strcat(temp, fpath); + // Let rmdir() handle the error if directory doesn't exist auto rc = ::rmdir(temp); free(temp); return rc == 0; @@ -271,29 +236,30 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs return; } - if (!stat(temp, &_stat)) { - //file found - if (S_ISREG(_stat.st_mode)) { - _isDirectory = false; - _f = fopen(temp, mode); - if (!_f) { - log_e("fopen(%s) failed", temp); - } - if (_f && (_stat.st_blksize == 0)) { - setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); - } - } else if (S_ISDIR(_stat.st_mode)) { - _isDirectory = true; - _d = opendir(temp); - if (!_d) { - log_e("opendir(%s) failed", temp); + // For read mode, check if file exists first to determine type + if (!mode || mode[0] == 'r') { + if (!stat(temp, &_stat)) { + //file found + if (S_ISREG(_stat.st_mode)) { + _isDirectory = false; + _f = fopen(temp, mode); + if (!_f) { + log_e("fopen(%s) failed", temp); + } + if (_f && (_stat.st_blksize == 0)) { + setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); + } + } else if (S_ISDIR(_stat.st_mode)) { + _isDirectory = true; + _d = opendir(temp); + if (!_d) { + log_e("opendir(%s) failed", temp); + } + } else { + log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp); } } else { - log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp); - } - } else { - //file not found - if (!mode || mode[0] == 'r') { + //file not found //try to open as directory _d = opendir(temp); if (_d) { @@ -302,16 +268,20 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs _isDirectory = false; //log_w("stat(%s) failed", temp); } - } else { - //lets create this new file - _isDirectory = false; - _f = fopen(temp, mode); - if (!_f) { - log_e("fopen(%s) failed", temp); - } + } + } else { + //lets create this new file + _isDirectory = false; + _f = fopen(temp, mode); + if (!_f) { + log_e("fopen(%s) failed", temp); + } + if (!stat(temp, &_stat)) { if (_f && (_stat.st_blksize == 0)) { setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); } + } else { + log_e("stat(%s) failed", temp); } } free(temp); diff --git a/libraries/HTTPClient/examples/Authorization/Authorization.ino b/libraries/HTTPClient/examples/Authorization/Authorization.ino index 02bf9ae93c7..91ba7c1a1a6 100644 --- a/libraries/HTTPClient/examples/Authorization/Authorization.ino +++ b/libraries/HTTPClient/examples/Authorization/Authorization.ino @@ -12,21 +12,19 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -39,7 +37,7 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure traged server and url http.begin("http://user:password@192.168.1.12/test.html"); @@ -54,22 +52,22 @@ void loop() { http.setAuthorization("dXNlcjpwYXN3b3Jk"); */ - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - USE_SERIAL.println(payload); + Serial.println(payload); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/Authorization/ci.json b/libraries/HTTPClient/examples/Authorization/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/Authorization/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPClient/examples/Authorization/ci.yml b/libraries/HTTPClient/examples/Authorization/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/Authorization/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino b/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino index e8f5be62438..a3a1f317d6c 100644 --- a/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino +++ b/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino @@ -12,8 +12,6 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; /* @@ -49,15 +47,15 @@ const char* ca = \ void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -70,27 +68,27 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure traged server and url //http.begin("https://www.howsmyssl.com/a/check", ca); //HTTPS http.begin("http://example.com/index.html"); //HTTP - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - USE_SERIAL.println(payload); + Serial.println(payload); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/BasicHttpClient/ci.json b/libraries/HTTPClient/examples/BasicHttpClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/BasicHttpClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPClient/examples/BasicHttpClient/ci.yml b/libraries/HTTPClient/examples/BasicHttpClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/BasicHttpClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index 73e127d1261..5d173da8fc0 100644 --- a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -59,7 +59,8 @@ void setClock() { struct tm timeinfo; gmtime_r(&nowSecs, &timeinfo); Serial.print(F("Current time: ")); - Serial.print(asctime(&timeinfo)); + char buf[26]; + Serial.print(asctime_r(&timeinfo, buf)); } WiFiMulti WiFiMulti; diff --git a/libraries/HTTPClient/examples/BasicHttpsClient/ci.json b/libraries/HTTPClient/examples/BasicHttpsClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/BasicHttpsClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPClient/examples/BasicHttpsClient/ci.yml b/libraries/HTTPClient/examples/BasicHttpsClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/BasicHttpsClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino b/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino new file mode 100644 index 00000000000..a73af191fcf --- /dev/null +++ b/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino @@ -0,0 +1,83 @@ +#include + +#include +#include + +// Enable or disable collecting all headers +#define COLLECT_ALL_HEADERS true + +void setup() { + + Serial.begin(115200); + + Serial.println(); + Serial.println(); + Serial.println(); + + for (uint8_t t = 4; t > 0; t--) { + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); + delay(1000); + } + + WiFi.begin("SSID", "PASSWORD"); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + Serial.println("Connected to WiFi: " + WiFi.SSID()); +} + +void loop() { + + HTTPClient http; + + Serial.print("[HTTP] Preparing HTTP request...\n"); + // This page will return the headers we want to test + some others + http.begin("https://httpbingo.org/response-headers?x-custom-header=value:42"); + +#if COLLECT_ALL_HEADERS + // Collect all headers + http.collectAllHeaders(); +#else + // Collect specific headers, only that one will be stored + const char *headerKeys[] = {"x-custom-header"}; + const size_t headerKeysCount = sizeof(headerKeys) / sizeof(headerKeys[0]); + http.collectHeaders(headerKeys, headerKeysCount); +#endif + + Serial.print("[HTTP] Sending HTTP GET request...\n"); + // start connection and send HTTP header + int httpCode = http.GET(); + + // httpCode will be negative on error + if (httpCode > 0) { + // HTTP header has been send and Server response header has been handled + Serial.printf("[HTTP] GET response code: %d\n", httpCode); + + Serial.println("[HTTP] Headers collected:"); + for (size_t i = 0; i < http.headers(); i++) { + Serial.printf("[HTTP] - '%s': '%s'\n", http.headerName(i).c_str(), http.header(i).c_str()); + } + + Serial.println("[HTTP] Has header 'x-custom-header'? " + String(http.hasHeader("x-custom-header")) + " (expected true)"); + Serial.printf("[HTTP] x-custom-header: '%s' (expected 'value:42')\n", http.header("x-custom-header").c_str()); + Serial.printf("[HTTP] non-existing-header: '%s' (expected empty string)\n", http.header("non-existing-header").c_str()); + +#if COLLECT_ALL_HEADERS + // Server response with multiple headers, one of them is 'server' + Serial.println("[HTTP] Has header 'server'? " + String(http.hasHeader("server")) + " (expected true)"); + Serial.printf("[HTTP] server: '%s'\n", http.header("server").c_str()); +#endif + + } else { + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + + http.end(); + + Serial.println("[HTTP] end connection\n\n"); + delay(5000); +} diff --git a/libraries/HTTPClient/examples/CustomHeaders/ci.yml b/libraries/HTTPClient/examples/CustomHeaders/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/CustomHeaders/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.json b/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.json deleted file mode 100644 index 04eb62b977a..00000000000 --- a/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.yml b/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.yml new file mode 100644 index 00000000000..e412162e577 --- /dev/null +++ b/libraries/HTTPClient/examples/HTTPClientEnterprise/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino b/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino index 5b3137a0179..7d7d27332e6 100644 --- a/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino +++ b/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino @@ -12,23 +12,21 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; HTTPClient http; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -47,14 +45,14 @@ void loop() { int httpCode = http.GET(); if (httpCode > 0) { - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { - http.writeToStream(&USE_SERIAL); + http.writeToStream(&Serial); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/ReuseConnection/ci.json b/libraries/HTTPClient/examples/ReuseConnection/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/ReuseConnection/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPClient/examples/ReuseConnection/ci.yml b/libraries/HTTPClient/examples/ReuseConnection/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/ReuseConnection/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino b/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino index 187526f6300..db7a9fc2622 100644 --- a/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino +++ b/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino @@ -12,21 +12,19 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -39,18 +37,18 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure server and url http.begin("http://192.168.1.12/test.html"); //http.begin("192.168.1.12", 80, "/test.html"); - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { @@ -74,7 +72,7 @@ void loop() { int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); // write it to Serial - USE_SERIAL.write(buff, c); + Serial.write(buff, c); if (len > 0) { len -= c; @@ -83,11 +81,11 @@ void loop() { delay(1); } - USE_SERIAL.println(); - USE_SERIAL.print("[HTTP] connection closed or file end.\n"); + Serial.println(); + Serial.print("[HTTP] connection closed or file end.\n"); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/StreamHttpClient/ci.json b/libraries/HTTPClient/examples/StreamHttpClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/StreamHttpClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPClient/examples/StreamHttpClient/ci.yml b/libraries/HTTPClient/examples/StreamHttpClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPClient/examples/StreamHttpClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPClient/library.properties b/libraries/HTTPClient/library.properties index bb5b0936255..ffbcc9101be 100644 --- a/libraries/HTTPClient/library.properties +++ b/libraries/HTTPClient/library.properties @@ -1,5 +1,5 @@ name=HTTPClient -version=3.3.0 +version=3.3.4 author=Markus Sattler maintainer=Markus Sattler sentence=HTTP Client for ESP32 diff --git a/libraries/HTTPClient/src/HTTPClient.cpp b/libraries/HTTPClient/src/HTTPClient.cpp index ec812f07201..8061042bd3f 100644 --- a/libraries/HTTPClient/src/HTTPClient.cpp +++ b/libraries/HTTPClient/src/HTTPClient.cpp @@ -90,9 +90,6 @@ HTTPClient::~HTTPClient() { if (_client) { _client->stop(); } - if (_currentHeaders) { - delete[] _currentHeaders; - } if (_tcpDeprecated) { _tcpDeprecated.reset(nullptr); } @@ -564,9 +561,12 @@ int HTTPClient::sendRequest(const char *type, uint8_t *payload, size_t size) { bool redirect = false; uint16_t redirectCount = 0; do { - // wipe out any existing headers from previous request - for (size_t i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].value.length() > 0) { + // wipe out any existing headers from previous request, but preserve the keys if collecting specific headers + if (_collectAllHeaders) { + _currentHeaders.clear(); + } else { + // Only clear values, keep the keys for specific header collection + for (size_t i = 0; i < _currentHeaders.size(); ++i) { _currentHeaders[i].value.clear(); } } @@ -1015,19 +1015,24 @@ void HTTPClient::addHeader(const String &name, const String &value, bool first, } } +void HTTPClient::collectAllHeaders(bool collectAll) { + _collectAllHeaders = collectAll; +} + void HTTPClient::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount; - if (_currentHeaders) { - delete[] _currentHeaders; + if (_collectAllHeaders) { + log_w("collectHeaders is ignored when collectAllHeaders is set"); + return; } - _currentHeaders = new RequestArgument[_headerKeysCount]; - for (size_t i = 0; i < _headerKeysCount; i++) { + _currentHeaders.clear(); + _currentHeaders.resize(headerKeysCount); + for (size_t i = 0; i < headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } } String HTTPClient::header(const char *name) { - for (size_t i = 0; i < _headerKeysCount; ++i) { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { if (_currentHeaders[i].key.equalsIgnoreCase(name)) { return _currentHeaders[i].value; } @@ -1036,25 +1041,25 @@ String HTTPClient::header(const char *name) { } String HTTPClient::header(size_t i) { - if (i < _headerKeysCount) { + if (i < _currentHeaders.size()) { return _currentHeaders[i].value; } return String(); } String HTTPClient::headerName(size_t i) { - if (i < _headerKeysCount) { + if (i < _currentHeaders.size()) { return _currentHeaders[i].key; } return String(); } int HTTPClient::headers() { - return _headerKeysCount; + return _currentHeaders.size(); } bool HTTPClient::hasHeader(const char *name) { - for (size_t i = 0; i < _headerKeysCount; ++i) { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) { return true; } @@ -1238,17 +1243,14 @@ int HTTPClient::handleHeaderResponse() { setCookie(date, headerValue); } - for (size_t i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - // Uncomment the following lines if you need to add support for multiple headers with the same key: - // if (!_currentHeaders[i].value.isEmpty()) { - // // Existing value, append this one with a comma - // _currentHeaders[i].value += ','; - // _currentHeaders[i].value += headerValue; - // } else { - _currentHeaders[i].value = headerValue; - // } - break; // We found a match, stop looking + if (_collectAllHeaders && headerName.length() > 0) { + _currentHeaders.emplace_back(headerName, headerValue); + } else { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { + _currentHeaders[i].value = headerValue; + break; // We found a match, stop looking + } } } } @@ -1593,8 +1595,9 @@ void HTTPClient::setCookie(String date, String headerValue) { // overwrite or delete cookie in/from cookie jar time_t now_local = time(NULL); - time_t now_gmt = mktime(gmtime(&now_local)); - + struct tm tm_gmt; + gmtime_r(&now_local, &tm_gmt); + time_t now_gmt = mktime(&tm_gmt); bool found = false; for (auto c = _cookieJar->begin(); c != _cookieJar->end(); ++c) { @@ -1619,8 +1622,9 @@ void HTTPClient::setCookie(String date, String headerValue) { bool HTTPClient::generateCookieString(String *cookieString) { time_t now_local = time(NULL); - time_t now_gmt = mktime(gmtime(&now_local)); - + struct tm tm_gmt; + gmtime_r(&now_local, &tm_gmt); + time_t now_gmt = mktime(&tm_gmt); *cookieString = ""; bool found = false; diff --git a/libraries/HTTPClient/src/HTTPClient.h b/libraries/HTTPClient/src/HTTPClient.h index 80f6da28599..e07cd937d06 100644 --- a/libraries/HTTPClient/src/HTTPClient.h +++ b/libraries/HTTPClient/src/HTTPClient.h @@ -38,7 +38,7 @@ #include #endif // HTTPCLIENT_NOSECURE -/// Cookie jar support +/// Cookie jar and header support #include #define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000) @@ -238,6 +238,7 @@ class HTTPClient { void addHeader(const String &name, const String &value, bool first = false, bool replace = true); /// Response handling + void collectAllHeaders(bool collectAll = true); void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); String header(const char *name); // get request header value by name String header(size_t i); // get request header value by number @@ -294,6 +295,7 @@ class HTTPClient { uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; bool _useHTTP10 = false; bool _secure = false; + bool _collectAllHeaders = false; String _uri; String _protocol; @@ -304,8 +306,7 @@ class HTTPClient { String _acceptEncoding = "identity;q=1,chunked;q=0.1,*;q=0"; /// Response handling - RequestArgument *_currentHeaders = nullptr; - size_t _headerKeysCount = 0; + std::vector _currentHeaders; int _returnCode = 0; int _size = -1; diff --git a/libraries/HTTPUpdate/examples/httpUpdate/ci.json b/libraries/HTTPUpdate/examples/httpUpdate/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPUpdate/examples/httpUpdate/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPUpdate/examples/httpUpdate/ci.yml b/libraries/HTTPUpdate/examples/httpUpdate/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPUpdate/examples/httpUpdate/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.json b/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.yml b/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPUpdate/examples/httpUpdateSPIFFS/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.json b/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.json deleted file mode 100644 index cbdd28f773d..00000000000 --- a/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.yml b/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.yml new file mode 100644 index 00000000000..9f15b3468e6 --- /dev/null +++ b/libraries/HTTPUpdate/examples/httpUpdateSecure/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino index d349c6983c1..c542985d71d 100644 --- a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino +++ b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino @@ -32,7 +32,8 @@ void setClock() { struct tm timeinfo; gmtime_r(&now, &timeinfo); Serial.print(F("Current time: ")); - Serial.print(asctime(&timeinfo)); + char buf[26]; + Serial.print(asctime_r(&timeinfo, buf)); } /** diff --git a/libraries/HTTPUpdate/library.properties b/libraries/HTTPUpdate/library.properties index 88466a3a72e..dcf5f1219d7 100644 --- a/libraries/HTTPUpdate/library.properties +++ b/libraries/HTTPUpdate/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdate -version=3.3.0 +version=3.3.4 author=Markus Sattler maintainer=Markus Sattler sentence=Http Update for ESP32 diff --git a/libraries/HTTPUpdate/src/HTTPUpdate.cpp b/libraries/HTTPUpdate/src/HTTPUpdate.cpp index b463ffe2e83..5183afac017 100644 --- a/libraries/HTTPUpdate/src/HTTPUpdate.cpp +++ b/libraries/HTTPUpdate/src/HTTPUpdate.cpp @@ -52,11 +52,31 @@ HTTPUpdateResult HTTPUpdate::update(NetworkClient &client, const String &url, co if (!http.begin(client, url)) { return HTTP_UPDATE_FAILED; } - return handleUpdate(http, currentVersion, false, requestCB); + return handleUpdate(http, currentVersion, U_FLASH, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateFs(HTTPClient &httpClient, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + return handleUpdate(httpClient, currentVersion, U_FLASHFS, requestCB); } HTTPUpdateResult HTTPUpdate::updateSpiffs(HTTPClient &httpClient, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { - return handleUpdate(httpClient, currentVersion, true, requestCB); + return handleUpdate(httpClient, currentVersion, U_SPIFFS, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateFatfs(HTTPClient &httpClient, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + return handleUpdate(httpClient, currentVersion, U_FATFS, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateLittlefs(HTTPClient &httpClient, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + return handleUpdate(httpClient, currentVersion, U_LITTLEFS, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateFs(NetworkClient &client, const String &url, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + HTTPClient http; + if (!http.begin(client, url)) { + return HTTP_UPDATE_FAILED; + } + return handleUpdate(http, currentVersion, U_FLASHFS, requestCB); } HTTPUpdateResult HTTPUpdate::updateSpiffs(NetworkClient &client, const String &url, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { @@ -64,11 +84,27 @@ HTTPUpdateResult HTTPUpdate::updateSpiffs(NetworkClient &client, const String &u if (!http.begin(client, url)) { return HTTP_UPDATE_FAILED; } - return handleUpdate(http, currentVersion, true, requestCB); + return handleUpdate(http, currentVersion, U_SPIFFS, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateFatfs(NetworkClient &client, const String &url, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + HTTPClient http; + if (!http.begin(client, url)) { + return HTTP_UPDATE_FAILED; + } + return handleUpdate(http, currentVersion, U_FATFS, requestCB); +} + +HTTPUpdateResult HTTPUpdate::updateLittlefs(NetworkClient &client, const String &url, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { + HTTPClient http; + if (!http.begin(client, url)) { + return HTTP_UPDATE_FAILED; + } + return handleUpdate(http, currentVersion, U_LITTLEFS, requestCB); } HTTPUpdateResult HTTPUpdate::update(HTTPClient &httpClient, const String ¤tVersion, HTTPUpdateRequestCB requestCB) { - return handleUpdate(httpClient, currentVersion, false, requestCB); + return handleUpdate(httpClient, currentVersion, U_FLASH, requestCB); } HTTPUpdateResult @@ -77,7 +113,7 @@ HTTPUpdateResult if (!http.begin(client, host, port, uri)) { return HTTP_UPDATE_FAILED; } - return handleUpdate(http, currentVersion, false, requestCB); + return handleUpdate(http, currentVersion, U_FLASH, requestCB); } /** @@ -158,7 +194,7 @@ String getSketchSHA256() { * @param currentVersion const char * * @return HTTPUpdateResult */ -HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤tVersion, bool spiffs, HTTPUpdateRequestCB requestCB) { +HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤tVersion, uint8_t type, HTTPUpdateRequestCB requestCB) { HTTPUpdateResult ret = HTTP_UPDATE_FAILED; @@ -187,8 +223,14 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ http.addHeader("x-ESP32-chip-size", String(ESP.getFlashChipSize())); http.addHeader("x-ESP32-sdk-version", ESP.getSdkVersion()); - if (spiffs) { + if (type == U_SPIFFS) { http.addHeader("x-ESP32-mode", "spiffs"); + } else if (type == U_FATFS) { + http.addHeader("x-ESP32-mode", "fatfs"); + } else if (type == U_LITTLEFS) { + http.addHeader("x-ESP32-mode", "littlefs"); + } else if (type == U_FLASHFS) { + http.addHeader("x-ESP32-mode", "flashfs"); } else { http.addHeader("x-ESP32-mode", "sketch"); } @@ -251,8 +293,24 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ case HTTP_CODE_OK: ///< OK (Start Update) if (len > 0) { bool startUpdate = true; - if (spiffs) { - const esp_partition_t *_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + if (type != U_FLASH) { + const esp_partition_t *_partition = NULL; + if (type == U_SPIFFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + } else if (type == U_FATFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + } else if (type == U_LITTLEFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, NULL); + } else if (type == U_FLASHFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + if (!_partition) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + } + if (!_partition) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, NULL); + } + } + if (!_partition) { _lastError = HTTP_UE_NO_PARTITION; return HTTP_UPDATE_FAILED; @@ -291,17 +349,15 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ delay(100); - int command; + int command = type; - if (spiffs) { - command = U_SPIFFS; - log_d("runUpdate spiffs...\n"); - } else { - command = U_FLASH; + if (type == U_FLASH) { log_d("runUpdate flash...\n"); + } else { + log_d("runUpdate file system...\n"); } - if (!spiffs) { + if (type == U_FLASH) { /* To do uint8_t buf[4]; if(tcp->peekBytes(&buf[0], 4) != 4) { @@ -341,7 +397,7 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ _cbEnd(); } - if (_rebootOnUpdate && !spiffs) { + if (_rebootOnUpdate && type == U_FLASH) { ESP.restart(); } diff --git a/libraries/HTTPUpdate/src/HTTPUpdate.h b/libraries/HTTPUpdate/src/HTTPUpdate.h index a48d1a89a3e..ad38701b948 100644 --- a/libraries/HTTPUpdate/src/HTTPUpdate.h +++ b/libraries/HTTPUpdate/src/HTTPUpdate.h @@ -98,11 +98,17 @@ class HTTPUpdate { NetworkClient &client, const String &host, uint16_t port, const String &uri = "/", const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL ); + t_httpUpdate_return updateFs(NetworkClient &client, const String &url, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); t_httpUpdate_return updateSpiffs(NetworkClient &client, const String &url, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return updateFatfs(NetworkClient &client, const String &url, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return updateLittlefs(NetworkClient &client, const String &url, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); t_httpUpdate_return update(HTTPClient &httpClient, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return updateFs(HTTPClient &httpClient, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); t_httpUpdate_return updateSpiffs(HTTPClient &httpClient, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return updateFatfs(HTTPClient &httpClient, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return updateLittlefs(HTTPClient &httpClient, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); // Notification callbacks void onStart(HTTPUpdateStartCB cbOnStart) { @@ -122,7 +128,7 @@ class HTTPUpdate { String getLastErrorString(void); protected: - t_httpUpdate_return handleUpdate(HTTPClient &http, const String ¤tVersion, bool spiffs = false, HTTPUpdateRequestCB requestCB = NULL); + t_httpUpdate_return handleUpdate(HTTPClient &http, const String ¤tVersion, uint8_t type = U_FLASH, HTTPUpdateRequestCB requestCB = NULL); bool runUpdate(Stream &in, uint32_t size, String md5, int command = U_FLASH); // Set the error and potentially use a CB to notify the application diff --git a/libraries/HTTPUpdateServer/examples/WebUpdater/ci.json b/libraries/HTTPUpdateServer/examples/WebUpdater/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPUpdateServer/examples/WebUpdater/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/HTTPUpdateServer/examples/WebUpdater/ci.yml b/libraries/HTTPUpdateServer/examples/WebUpdater/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/HTTPUpdateServer/examples/WebUpdater/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/HTTPUpdateServer/library.properties b/libraries/HTTPUpdateServer/library.properties index c182eeb8d7f..bd034861f1e 100644 --- a/libraries/HTTPUpdateServer/library.properties +++ b/libraries/HTTPUpdateServer/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdateServer -version=3.3.0 +version=3.3.4 author=Hristo Kapanakov maintainer= sentence=Simple HTTP Update server based on the WebServer diff --git a/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h index 65d8cbaa783..d9e9b9ea2d5 100644 --- a/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h +++ b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h @@ -1,7 +1,6 @@ #ifndef __HTTP_UPDATE_SERVER_H #define __HTTP_UPDATE_SERVER_H -#include #include #include #include @@ -123,7 +122,7 @@ class HTTPUpdateServer { Serial.printf("Update: %s\n", upload.filename.c_str()); } if (upload.name == "filesystem") { - if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) { //start with max available size + if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASHFS)) { //Instead of SPIFFS.totalBytes(). Fix https://github.com/espressif/arduino-esp32/issues/9967 if (_serial_output) { Update.printError(Serial); } diff --git a/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino b/libraries/Hash/examples/HEX/HEX.ino similarity index 94% rename from libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino rename to libraries/Hash/examples/HEX/HEX.ino index f580a763b54..4ad78db3ad9 100644 --- a/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino +++ b/libraries/Hash/examples/HEX/HEX.ino @@ -1,3 +1,9 @@ +/* + Usage example for the HEXBuilder class. + + This example shows how to convert a HEX string to a binary buffer and vice versa. +*/ + #include void setup() { diff --git a/libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino b/libraries/Hash/examples/MD5/MD5.ino similarity index 100% rename from libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino rename to libraries/Hash/examples/MD5/MD5.ino diff --git a/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino b/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino new file mode 100644 index 00000000000..9a37447add5 --- /dev/null +++ b/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino @@ -0,0 +1,178 @@ +/* + Usage example for the PBKDF2_HMACBuilder class. + + This example shows how to use the Hash library to hash data using the PBKDF2_HMACBuilder class. + PBKDF2_HMAC (Password-Based Key Derivation Function 2) is a key derivation function that uses a password and a salt to derive a key. + + The PBKDF2_HMACBuilder class takes for arguments: + - A HashBuilder object to use for the HMAC (SHA1Builder, SHA2Builder, SHA3Builder, etc.) + - A password string (default: empty) + - A salt string (default: empty) + - The number of iterations (default: 1000) +*/ + +#include +#include +#include + +void setup() { + Serial.begin(115200); + Serial.println("\n\nPBKDF2-HMAC Example"); + Serial.println("==================="); + + // Test 1: Basic PBKDF2-HMAC-SHA1 + Serial.println("\n1. PBKDF2-HMAC-SHA1 Test (1 iteration)"); + { + SHA1Builder sha1; + PBKDF2_HMACBuilder pbkdf2(&sha1, "password", "salt", 1); + + pbkdf2.begin(); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println("password"); + Serial.print("Salt: "); + Serial.println("salt"); + Serial.print("Iterations: "); + Serial.println(1); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 + String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 2: PBKDF2-HMAC-SHA1 with more iterations + Serial.println("\n2. PBKDF2-HMAC-SHA1 Test (1000 iterations)"); + { + SHA1Builder sha1; + PBKDF2_HMACBuilder pbkdf2(&sha1); + + const char *password = "password"; + const char *salt = "salt"; + + pbkdf2.begin(); + pbkdf2.setPassword(password); + pbkdf2.setSalt(salt); + pbkdf2.setIterations(1000); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println(password); + Serial.print("Salt: "); + Serial.println(salt); + Serial.print("Iterations: "); + Serial.println(1000); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 6e88be8bad7eae9d9e10aa061224034fed48d03f + String expected = "6e88be8bad7eae9d9e10aa061224034fed48d03f"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 3: PBKDF2-HMAC-SHA256 with different password and salt + Serial.println("\n3. PBKDF2-HMAC-SHA256 Test"); + { + SHA256Builder sha256; + PBKDF2_HMACBuilder pbkdf2(&sha256, "mySecretPassword", "randomSalt123", 100); + + pbkdf2.begin(); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println("mySecretPassword"); + Serial.print("Salt: "); + Serial.println("randomSalt123"); + Serial.print("Iterations: "); + Serial.println(100); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8 + String expected = "4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 4: PBKDF2-HMAC-SHA1 with byte arrays + Serial.println("\n4. PBKDF2-HMAC-SHA1 Test (byte arrays)"); + { + SHA1Builder sha1; // or any other hash algorithm based on HashBuilder + PBKDF2_HMACBuilder pbkdf2(&sha1); + + uint8_t password[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}; // "password" in bytes + uint8_t salt[] = {0x73, 0x61, 0x6c, 0x74}; // "salt" in bytes + + pbkdf2.begin(); + pbkdf2.setPassword(password, sizeof(password)); + pbkdf2.setSalt(salt, sizeof(salt)); + pbkdf2.setIterations(1); + pbkdf2.calculate(); + + Serial.print("Password (bytes): "); + for (int i = 0; i < sizeof(password); i++) { + Serial.print((char)password[i]); + } + Serial.println(); + Serial.print("Salt (bytes): "); + for (int i = 0; i < sizeof(salt); i++) { + Serial.print((char)salt[i]); + } + Serial.println(); + Serial.print("Iterations: "); + Serial.println(1); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 (same as test 1) + String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + Serial.println("\nPBKDF2-HMAC tests completed!"); +} + +void loop() { + // Nothing to do in loop +} diff --git a/libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino b/libraries/Hash/examples/SHA1/SHA1.ino similarity index 100% rename from libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino rename to libraries/Hash/examples/SHA1/SHA1.ino diff --git a/libraries/Hash/examples/SHA2/SHA2.ino b/libraries/Hash/examples/SHA2/SHA2.ino new file mode 100644 index 00000000000..932ea51565e --- /dev/null +++ b/libraries/Hash/examples/SHA2/SHA2.ino @@ -0,0 +1,95 @@ +/* + Usage example for the SHA2Builder class. + + This example shows how to use the SHA2 library to hash data using the SHA2Builder class. + SHA2 (Secure Hash Algorithm 2) provides different output sizes: SHA-224, SHA-256, SHA-384, and SHA-512. + + Available constructors: + - SHA224Builder(): 224-bit hash output + - SHA256Builder(): 256-bit hash output + - SHA384Builder(): 384-bit hash output + - SHA512Builder(): 512-bit hash output + - SHA2Builder(size_t hash_size): Generic class that can be used to create any SHA2 variant implemented +*/ + +#include + +// Expected hash values for validation +const char *EXPECTED_HELLO_WORLD_SHA256 = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"; +const char *EXPECTED_HELLO_WORLD_SHA512 = + "2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"; +const char *EXPECTED_TEST_MESSAGE_SHA224 = "155b033d801d4dd59b783d76ac3059053c00b2c28340a5a36a427a76"; +const char *EXPECTED_TEST_MESSAGE_SHA384 = "efd336618cbc96551936e5897e6af391d2480513ff8d4fc744e34462edb3111477d2b889c4d5e80e23b5f9d1b636fbd7"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +void setup() { + Serial.begin(115200); + + Serial.println("\n\n\nStart."); + + // Using SHA2Builder class directly with different hash sizes + { + String test_data = "Hello World"; + Serial.println("Test data: " + test_data); + + // Create SHA-256 (default hash size) + SHA2Builder sha2_256; + sha2_256.begin(); + sha2_256.add(test_data); + sha2_256.calculate(); + String hash_256 = sha2_256.toString(); + validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA256, "SHA-256 validation"); + + // Create SHA-512 + SHA2Builder sha2_512(SHA2_512_HASH_SIZE); + sha2_512.begin(); + sha2_512.add(test_data); + sha2_512.calculate(); + String hash_512 = sha2_512.toString(); + validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA512, "SHA-512 validation"); + } + + // Example using SHA224Builder and SHA384Builder + // There are other constructors for other hash sizes available: + // - SHA224Builder() + // - SHA256Builder() + // - SHA384Builder() + // - SHA512Builder() + // - SHA2Builder(size_t hash_size) + { + String test_data = "Test message"; + Serial.println("Test data: " + test_data); + + // Create SHA-224 using specific constructor + SHA224Builder sha2_224; + sha2_224.begin(); + sha2_224.add(test_data); + sha2_224.calculate(); + String hash_224 = sha2_224.toString(); + validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA224, "SHA224Builder validation"); + + // Create SHA-384 using specific constructor + SHA384Builder sha2_384; + sha2_384.begin(); + sha2_384.add(test_data); + sha2_384.calculate(); + String hash_384 = sha2_384.toString(); + validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA384, "SHA384Builder validation"); + } + + Serial.println("Done."); +} + +void loop() {} diff --git a/libraries/Hash/examples/SHA3/SHA3.ino b/libraries/Hash/examples/SHA3/SHA3.ino new file mode 100644 index 00000000000..258559d07f4 --- /dev/null +++ b/libraries/Hash/examples/SHA3/SHA3.ino @@ -0,0 +1,95 @@ +/* + Usage example for the SHA3Builder class. + + This example shows how to use the SHA3 library to hash data using the SHA3Builder class. + SHA3 (Secure Hash Algorithm 3) provides different output sizes: SHA3-224, SHA3-256, SHA3-384, and SHA3-512. + + Available constructors: + - SHA3_224Builder(): 224-bit hash output + - SHA3_256Builder(): 256-bit hash output + - SHA3_384Builder(): 384-bit hash output + - SHA3_512Builder(): 512-bit hash output + - SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented +*/ + +#include + +// Expected hash values for validation +const char *EXPECTED_HELLO_WORLD_SHA3_256 = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"; +const char *EXPECTED_HELLO_WORLD_SHA3_512 = + "3d58a719c6866b0214f96b0a67b37e51a91e233ce0be126a08f35fdf4c043c6126f40139bfbc338d44eb2a03de9f7bb8eff0ac260b3629811e389a5fbee8a894"; +const char *EXPECTED_TEST_MESSAGE_SHA3_224 = "27af391bcb3b86f21b73c42c4abbde4791c395dc650243eede85de0c"; +const char *EXPECTED_TEST_MESSAGE_SHA3_384 = "adb18f6b164672c566950bfefa48c5a851d48ee184f249a19e723d753b7536fcd048c3443aff7ebe433fce63c81726ea"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +void setup() { + Serial.begin(115200); + + Serial.println("\n\n\nStart."); + + // Using SHA3Builder class directly with different hash sizes + { + String test_data = "Hello World"; + Serial.println("Test data: " + test_data); + + // Create SHA3-256 (default hash size) + SHA3Builder sha3_256; + sha3_256.begin(); + sha3_256.add(test_data); + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA3_256, "SHA3-256 validation"); + + // Create SHA3-512 + SHA3Builder sha3_512(SHA3_512_HASH_SIZE); + sha3_512.begin(); + sha3_512.add(test_data); + sha3_512.calculate(); + String hash_512 = sha3_512.toString(); + validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA3_512, "SHA3-512 validation"); + } + + // Example using SHA3_224Builder and SHA3_384Builder + // There are other constructors for other hash sizes available: + // - SHA3_224Builder() + // - SHA3_256Builder() + // - SHA3_384Builder() + // - SHA3_512Builder() + // - SHA3Builder(size_t hash_size) + { + String test_data = "Test message"; + Serial.println("Test data: " + test_data); + + // Create SHA3-224 using specific constructor + SHA3_224Builder sha3_224; + sha3_224.begin(); + sha3_224.add(test_data); + sha3_224.calculate(); + String hash_224 = sha3_224.toString(); + validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA3_224, "SHA3_224Builder validation"); + + // Create SHA3-384 using specific constructor + SHA3_384Builder sha3_384; + sha3_384.begin(); + sha3_384.add(test_data); + sha3_384.calculate(); + String hash_384 = sha3_384.toString(); + validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA3_384, "SHA3_384Builder validation"); + } + + Serial.println("Done."); +} + +void loop() {} diff --git a/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino b/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino new file mode 100644 index 00000000000..ba396b9bc1f --- /dev/null +++ b/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino @@ -0,0 +1,166 @@ +/* + Usage example for the SHA3Builder class with streams. + + This example shows how to use the SHA3 library to hash data from streams using the addStream method. + This is useful for hashing large files or data that comes from various stream sources like: + - File streams + - Network streams + - Memory streams + - Custom stream implementations + + Available constructors: + - SHA3_224Builder(): 224-bit hash output + - SHA3_256Builder(): 256-bit hash output + - SHA3_384Builder(): 384-bit hash output + - SHA3_512Builder(): 512-bit hash output + - SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented +*/ + +#include +#include + +// Expected hash values for validation +const char *EXPECTED_STREAM_TEST_SHA3_256 = "7094efc774885c7a785b408c5da86636cb8adc79156c0f162c6fd7e49f4c505e"; +const char *EXPECTED_MAX_SHA3_224_FULL = "ad0e69e04a7258d7cab4272a08ac69f8b43f4e45f9c49c9abb0628af"; +const char *EXPECTED_MAX_SHA3_224_10 = "9b55096e998cda6b96d3f2828c4ccda8c9964a1ad98989fb8b0fcd26"; +const char *EXPECTED_COMBINED_SHA3_256 = "4a32307fe03bf9f600c5d124419985fd4d42c1639e6a23ab044f107c3b95a189"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +// Custom stream class for demonstration +class TestStream : public Stream { +private: + String data; + size_t position; + +public: + TestStream(String input_data) : data(input_data), position(0) {} + + virtual int available() override { + return data.length() - position; + } + + virtual int read() override { + if (position < data.length()) { + return data.charAt(position++); + } + return -1; + } + + virtual int peek() override { + if (position < data.length()) { + return data.charAt(position); + } + return -1; + } + + virtual size_t write(uint8_t) override { + return 0; // Read-only stream + } + + size_t length() { + return data.length(); + } + + void reset() { + position = 0; + } +}; + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(10); + } + + Serial.println("\n\nSHA3 Stream Example"); + Serial.println("==================="); + + // Example 1: Using addStream with a custom stream + { + Serial.println("\n1. Hashing data from a custom stream:"); + + const char *test_data = "This is a test message for streaming hash calculation. " + "It contains multiple sentences to demonstrate how the " + "addStream method processes data in chunks."; + + TestStream stream(test_data); + + SHA3_256Builder sha3_256; + sha3_256.begin(); + + // Hash the entire stream + // First argument is the stream, second argument is the maximum length to be read from the stream + sha3_256.addStream(stream, stream.length()); // Reading the entire stream + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_STREAM_TEST_SHA3_256, "Stream test validation"); + } + + // Example 2: Using addStream with different maximum lengths + { + Serial.println("\n2. Comparing different maximum lengths with streams:"); + + const char *test_data = "Streaming hash test with different maximum lengths"; + TestStream stream(test_data); + + // SHA3-224 with a hardcoded maximum length + stream.reset(); + SHA3_224Builder sha3_224_10; + sha3_224_10.begin(); + sha3_224_10.addStream(stream, 10); // Passing a hardcoded maximum length to be read from the stream + sha3_224_10.calculate(); + String hash_224_10 = sha3_224_10.toString(); + validateHash(hash_224_10, EXPECTED_MAX_SHA3_224_10, "SHA3-224 with 10 bytes"); + + // SHA3-224 with the full stream + stream.reset(); + SHA3_224Builder sha3_224_full; + sha3_224_full.begin(); + sha3_224_full.addStream(stream, stream.length()); // Reading the entire stream + sha3_224_full.calculate(); + String hash_224_full = sha3_224_full.toString(); + validateHash(hash_224_full, EXPECTED_MAX_SHA3_224_FULL, "SHA3-224 with full stream"); + } + + // Example 3: Combining add() and addStream() + { + Serial.println("\n3. Combining add() and addStream():"); + + const char *stream_data = "Additional data from stream"; + TestStream stream(stream_data); + + SHA3_256Builder sha3_256; + sha3_256.begin(); + + // Add some data directly + sha3_256.add("Initial data: "); + + // Add data from stream + sha3_256.addStream(stream, stream.length()); + + // Add more data directly + sha3_256.add(" : Final data"); + + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_COMBINED_SHA3_256, "Combined data validation"); + } + + Serial.println("\nStream example completed!"); +} + +void loop() { + // Nothing to do in loop +} diff --git a/libraries/Hash/keywords.txt b/libraries/Hash/keywords.txt new file mode 100644 index 00000000000..d553b7b428e --- /dev/null +++ b/libraries/Hash/keywords.txt @@ -0,0 +1,51 @@ +####################################### +# Syntax Coloring Map For Hash +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +HashBuilder KEYWORD1 +HEXBuilder KEYWORD1 +MD5Builder KEYWORD1 +SHA1Builder KEYWORD1 +SHA2Builder KEYWORD1 +SHA224Builder KEYWORD1 +SHA256Builder KEYWORD1 +SHA384Builder KEYWORD1 +SHA512Builder KEYWORD1 +SHA3Builder KEYWORD1 +PBKDF2_HMACBuilder KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +add KEYWORD2 +addHexString KEYWORD2 +addStream KEYWORD2 +calculate KEYWORD2 +getBytes KEYWORD2 +getChars KEYWORD2 +toString KEYWORD2 +hex2bytes KEYWORD2 +bytes2hex KEYWORD2 +getHashSize KEYWORD2 +setPassword KEYWORD2 +setSalt KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SHA1_HASH_SIZE LITERAL1 +SHA2_224_HASH_SIZE LITERAL1 +SHA2_256_HASH_SIZE LITERAL1 +SHA2_384_HASH_SIZE LITERAL1 +SHA2_512_HASH_SIZE LITERAL1 +SHA3_224_HASH_SIZE LITERAL1 +SHA3_256_HASH_SIZE LITERAL1 +SHA3_384_HASH_SIZE LITERAL1 +SHA3_512_HASH_SIZE LITERAL1 diff --git a/libraries/Hash/library.properties b/libraries/Hash/library.properties new file mode 100644 index 00000000000..c497c42f71e --- /dev/null +++ b/libraries/Hash/library.properties @@ -0,0 +1,9 @@ +name=Hash +version=3.3.4 +author=lucasssvaz +maintainer=lucasssvaz +sentence=Bundle of hashing functions for the ESP32 +paragraph=This library provides a set of hashing functions to be used in the sketches +category=Security +url= +architectures=esp32 diff --git a/libraries/Hash/src/PBKDF2_HMACBuilder.cpp b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp new file mode 100644 index 00000000000..ec06bc642e5 --- /dev/null +++ b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp @@ -0,0 +1,258 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "PBKDF2_HMACBuilder.h" + +// Block size for HMAC (64 bytes for SHA-1, SHA-256, SHA-512) +#define HMAC_BLOCK_SIZE 64 + +PBKDF2_HMACBuilder::PBKDF2_HMACBuilder(HashBuilder *hash, String password, String salt, uint32_t iterations) { + this->hashBuilder = hash; + this->hashSize = hashBuilder->getHashSize(); + this->iterations = iterations; + + // Initialize pointers + this->password = nullptr; + this->salt = nullptr; + this->passwordLen = 0; + this->saltLen = 0; + this->derivedKey = nullptr; + this->derivedKeyLen = 0; + this->calculated = false; + + if (password.length() > 0) { + setPassword(password); + } + + if (salt.length() > 0) { + setSalt(salt); + } +} + +PBKDF2_HMACBuilder::~PBKDF2_HMACBuilder() { + clearData(); +} + +void PBKDF2_HMACBuilder::clearData() { + if (derivedKey != nullptr) { + forced_memzero(derivedKey, derivedKeyLen); + delete[] derivedKey; + derivedKey = nullptr; + } + derivedKeyLen = 0; + calculated = false; +} + +void PBKDF2_HMACBuilder::hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output) { + uint8_t keyPad[HMAC_BLOCK_SIZE]; + uint8_t outerPad[HMAC_BLOCK_SIZE]; + uint8_t innerHash[64]; // Large enough for any hash + + // Prepare key + if (keyLen > HMAC_BLOCK_SIZE) { + // Key is longer than block size, hash it + hashBuilder->begin(); + hashBuilder->add(key, keyLen); + hashBuilder->calculate(); + hashBuilder->getBytes(keyPad); + keyLen = hashSize; + } else { + // Copy key to keyPad + memcpy(keyPad, key, keyLen); + } + + // Pad key with zeros if necessary + if (keyLen < HMAC_BLOCK_SIZE) { + memset(keyPad + keyLen, 0, HMAC_BLOCK_SIZE - keyLen); + } + + // Create outer and inner pads + for (int i = 0; i < HMAC_BLOCK_SIZE; i++) { + outerPad[i] = keyPad[i] ^ 0x5c; + keyPad[i] = keyPad[i] ^ 0x36; + } + + // Inner hash: H(K XOR ipad, text) + hashBuilder->begin(); + hashBuilder->add(keyPad, HMAC_BLOCK_SIZE); + hashBuilder->add(data, dataLen); + hashBuilder->calculate(); + hashBuilder->getBytes(innerHash); + + // Outer hash: H(K XOR opad, inner_hash) + hashBuilder->begin(); + hashBuilder->add(outerPad, HMAC_BLOCK_SIZE); + hashBuilder->add(innerHash, hashSize); + hashBuilder->calculate(); + hashBuilder->getBytes(output); +} + +// HashBuilder interface methods +void PBKDF2_HMACBuilder::begin() { + clearData(); +} + +void PBKDF2_HMACBuilder::add(const uint8_t *data, size_t len) { + log_w("PBKDF2_HMACBuilder::add sets only the password. Use setPassword() and setSalt() instead."); + setPassword(data, len); +} + +bool PBKDF2_HMACBuilder::addStream(Stream &stream, const size_t maxLen) { + log_e("PBKDF2_HMACBuilder does not support addStream. Use setPassword() and setSalt() instead."); + (void)stream; + (void)maxLen; + return false; +} + +void PBKDF2_HMACBuilder::calculate() { + if (password == nullptr || salt == nullptr) { + log_e("Error: Password or salt not set."); + return; + } + + // Set default output size to hash size if not specified + if (derivedKeyLen == 0) { + derivedKeyLen = hashSize; + } + + // Allocate output buffer + if (derivedKey != nullptr) { + forced_memzero(derivedKey, derivedKeyLen); + delete[] derivedKey; + } + derivedKey = new uint8_t[derivedKeyLen]; + + // Perform PBKDF2-HMAC + pbkdf2_hmac(password, passwordLen, salt, saltLen, iterations, derivedKey, derivedKeyLen); + calculated = true; +} + +void PBKDF2_HMACBuilder::getBytes(uint8_t *output) { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return; + } + memcpy(output, derivedKey, derivedKeyLen); +} + +void PBKDF2_HMACBuilder::getChars(char *output) { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, derivedKeyLen * 2 + 1, derivedKey, derivedKeyLen); +} + +String PBKDF2_HMACBuilder::toString() { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return ""; + } + + char out[(derivedKeyLen * 2) + 1]; + getChars(out); + return String(out); +} + +// PBKDF2 specific methods +void PBKDF2_HMACBuilder::setPassword(const uint8_t *password, size_t len) { + if (this->password != nullptr) { + forced_memzero(this->password, len); + delete[] this->password; + } + this->password = new uint8_t[len]; + memcpy(this->password, password, len); + this->passwordLen = len; + calculated = false; +} + +void PBKDF2_HMACBuilder::setPassword(const char *password) { + setPassword((const uint8_t *)password, strlen(password)); +} + +void PBKDF2_HMACBuilder::setPassword(String password) { + setPassword((const uint8_t *)password.c_str(), password.length()); +} + +void PBKDF2_HMACBuilder::setSalt(const uint8_t *salt, size_t len) { + if (this->salt != nullptr) { + forced_memzero(this->salt, len); + delete[] this->salt; + } + this->salt = new uint8_t[len]; + memcpy(this->salt, salt, len); + this->saltLen = len; + calculated = false; +} + +void PBKDF2_HMACBuilder::setSalt(const char *salt) { + setSalt((const uint8_t *)salt, strlen(salt)); +} + +void PBKDF2_HMACBuilder::setSalt(String salt) { + setSalt((const uint8_t *)salt.c_str(), salt.length()); +} + +void PBKDF2_HMACBuilder::setIterations(uint32_t iterations) { + this->iterations = iterations; +} + +void PBKDF2_HMACBuilder::setHashAlgorithm(HashBuilder *hash) { + // Set the hash algorithm to use for the HMAC + // Note: We don't delete hashBuilder here as it might be owned by the caller + // The caller is responsible for managing the hashBuilder lifetime + hashBuilder = hash; + hashSize = hashBuilder->getHashSize(); +} + +void PBKDF2_HMACBuilder::pbkdf2_hmac( + const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen +) { + uint8_t u1[64]; // Large enough for any hash + uint8_t u2[64]; + uint8_t saltWithBlock[256]; // Salt + block number + uint8_t block[64]; + + size_t blocks = (outputLen + hashSize - 1) / hashSize; + + for (size_t i = 1; i <= blocks; i++) { + // Prepare salt || INT(i) + memcpy(saltWithBlock, salt, saltLen); + saltWithBlock[saltLen] = (i >> 24) & 0xFF; + saltWithBlock[saltLen + 1] = (i >> 16) & 0xFF; + saltWithBlock[saltLen + 2] = (i >> 8) & 0xFF; + saltWithBlock[saltLen + 3] = i & 0xFF; + + // U1 = HMAC(password, salt || INT(i)) + hmac(password, passwordLen, saltWithBlock, saltLen + 4, u1); + memcpy(block, u1, hashSize); + + // U2 = HMAC(password, U1) + for (uint32_t j = 1; j < iterations; j++) { + hmac(password, passwordLen, u1, hashSize, u2); + memcpy(u1, u2, hashSize); + + // XOR with previous result + for (size_t k = 0; k < hashSize; k++) { + block[k] ^= u1[k]; + } + } + + // Copy block to output + size_t copyLen = (i == blocks) ? (outputLen - (i - 1) * hashSize) : hashSize; + memcpy(output + (i - 1) * hashSize, block, copyLen); + } +} diff --git a/libraries/Hash/src/PBKDF2_HMACBuilder.h b/libraries/Hash/src/PBKDF2_HMACBuilder.h new file mode 100644 index 00000000000..2a4a477c1c5 --- /dev/null +++ b/libraries/Hash/src/PBKDF2_HMACBuilder.h @@ -0,0 +1,73 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PBKDF2_HMACBuilder_h +#define PBKDF2_HMACBuilder_h + +#include +#include +#include "HashBuilder.h" + +class PBKDF2_HMACBuilder : public HashBuilder { +private: + HashBuilder *hashBuilder; + size_t hashSize; + uint32_t iterations; + + // Password and salt storage + uint8_t *password; + size_t passwordLen; + uint8_t *salt; + size_t saltLen; + + // Output storage + uint8_t *derivedKey; + size_t derivedKeyLen; + bool calculated; + + void hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output); + void pbkdf2_hmac(const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen); + void clearData(); + +public: + using HashBuilder::add; + + // Constructor takes a hash builder instance + PBKDF2_HMACBuilder(HashBuilder *hash, String password = "", String salt = "", uint32_t iterations = 10000); + ~PBKDF2_HMACBuilder(); + + // Standard HashBuilder interface + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + size_t getHashSize() const override { + return derivedKeyLen; + } + + // PBKDF2 specific methods + void setPassword(const uint8_t *password, size_t len); + void setPassword(const char *password); + void setPassword(String password); + void setSalt(const uint8_t *salt, size_t len); + void setSalt(const char *salt); + void setSalt(String salt); + void setIterations(uint32_t iterations); + void setHashAlgorithm(HashBuilder *hash); +}; + +#endif diff --git a/cores/esp32/SHA1Builder.cpp b/libraries/Hash/src/SHA1Builder.cpp similarity index 88% rename from cores/esp32/SHA1Builder.cpp rename to libraries/Hash/src/SHA1Builder.cpp index 6bbe3ca83e0..ab9e6550513 100644 --- a/cores/esp32/SHA1Builder.cpp +++ b/libraries/Hash/src/SHA1Builder.cpp @@ -1,27 +1,21 @@ -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - * Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024 - */ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Based on mbed TLS (https://tls.mbed.org) #include -#include +#include "SHA1Builder.h" // 32-bit integer manipulation macros (big endian) @@ -201,6 +195,8 @@ void SHA1Builder::process(const uint8_t *data) { // Public methods void SHA1Builder::begin(void) { + finalized = false; + total[0] = 0; total[1] = 0; @@ -218,7 +214,7 @@ void SHA1Builder::add(const uint8_t *data, size_t len) { size_t fill; uint32_t left; - if (len == 0) { + if (finalized || len == 0) { return; } @@ -251,17 +247,6 @@ void SHA1Builder::add(const uint8_t *data, size_t len) { } } -void SHA1Builder::addHexString(const char *data) { - uint16_t len = strlen(data); - uint8_t *tmp = (uint8_t *)malloc(len / 2); - if (tmp == NULL) { - return; - } - hex2bytes(tmp, len / 2, data); - add(tmp, len / 2); - free(tmp); -} - bool SHA1Builder::addStream(Stream &stream, const size_t maxLen) { const int buf_size = 512; int maxLengthLeft = maxLen; @@ -306,6 +291,10 @@ void SHA1Builder::calculate(void) { uint32_t high, low; uint8_t msglen[8]; + if (finalized) { + return; + } + high = (total[0] >> 29) | (total[1] << 3); low = (total[0] << 3); @@ -323,6 +312,8 @@ void SHA1Builder::calculate(void) { PUT_UINT32_BE(state[2], hash, 8); PUT_UINT32_BE(state[3], hash, 12); PUT_UINT32_BE(state[4], hash, 16); + + finalized = true; } void SHA1Builder::getBytes(uint8_t *output) { @@ -330,6 +321,11 @@ void SHA1Builder::getBytes(uint8_t *output) { } void SHA1Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA1 not calculated or no output buffer provided."); + return; + } + bytes2hex(output, SHA1_HASH_SIZE * 2 + 1, hash, SHA1_HASH_SIZE); } diff --git a/cores/esp32/SHA1Builder.h b/libraries/Hash/src/SHA1Builder.h similarity index 88% rename from cores/esp32/SHA1Builder.h rename to libraries/Hash/src/SHA1Builder.h index b587e4fdc96..3cbf8aad96a 100644 --- a/cores/esp32/SHA1Builder.h +++ b/libraries/Hash/src/SHA1Builder.h @@ -28,23 +28,24 @@ class SHA1Builder : public HashBuilder { uint32_t state[5]; /* intermediate digest state */ unsigned char buffer[64]; /* data block being processed */ uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ + bool finalized; /* Whether hash has been finalized */ void process(const uint8_t *data); public: - void begin() override; - using HashBuilder::add; - void add(const uint8_t *data, size_t len) override; - - using HashBuilder::addHexString; - void addHexString(const char *data) override; + SHA1Builder() : finalized(false) {} + void begin() override; + void add(const uint8_t *data, size_t len) override; bool addStream(Stream &stream, const size_t maxLen) override; void calculate() override; void getBytes(uint8_t *output) override; void getChars(char *output) override; String toString() override; + size_t getHashSize() const override { + return SHA1_HASH_SIZE; + } }; #endif diff --git a/libraries/Hash/src/SHA2Builder.cpp b/libraries/Hash/src/SHA2Builder.cpp new file mode 100644 index 00000000000..878f0ef7c8b --- /dev/null +++ b/libraries/Hash/src/SHA2Builder.cpp @@ -0,0 +1,421 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "esp32-hal-log.h" +#include "SHA2Builder.h" + +// SHA-256 constants +static const uint32_t sha256_k[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +// SHA-512 constants +static const uint64_t sha512_k[80] = {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, + 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, + 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, + 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, + 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, + 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +// Macros for bit manipulation +#define ROTR32(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) +#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) +#define CH32(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define CH64(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ32(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define MAJ64(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0_32(x) (ROTR32(x, 2) ^ ROTR32(x, 13) ^ ROTR32(x, 22)) +#define EP0_64(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define EP1_32(x) (ROTR32(x, 6) ^ ROTR32(x, 11) ^ ROTR32(x, 25)) +#define EP1_64(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SIG0_32(x) (ROTR32(x, 7) ^ ROTR32(x, 18) ^ ((x) >> 3)) +#define SIG0_64(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ ((x) >> 7)) +#define SIG1_32(x) (ROTR32(x, 17) ^ ROTR32(x, 19) ^ ((x) >> 10)) +#define SIG1_64(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ ((x) >> 6)) + +// Byte order conversion +#define BYTESWAP32(x) ((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)) +#define BYTESWAP64(x) (((uint64_t)BYTESWAP32((uint32_t)((x) >> 32))) | (((uint64_t)BYTESWAP32((uint32_t)(x))) << 32)) + +// Constructor +SHA2Builder::SHA2Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false), total_length(0) { + // Determine block size and algorithm family + if (hash_size == SHA2_224_HASH_SIZE || hash_size == SHA2_256_HASH_SIZE) { + block_size = SHA2_256_BLOCK_SIZE; + is_sha512 = false; + } else if (hash_size == SHA2_384_HASH_SIZE || hash_size == SHA2_512_HASH_SIZE) { + block_size = SHA2_512_BLOCK_SIZE; + is_sha512 = true; + } else { + log_e("Invalid hash size: %d", hash_size); + block_size = 0; + is_sha512 = false; + } +} + +// Initialize the hash computation +void SHA2Builder::begin() { + // Clear the state and buffer + memset(state_32, 0, sizeof(state_32)); + memset(state_64, 0, sizeof(state_64)); + memset(buffer, 0, sizeof(buffer)); + buffer_size = 0; + finalized = false; + total_length = 0; + + // Initialize state based on algorithm + if (!is_sha512) { + // SHA-224/256 initial values + if (hash_size == SHA2_224_HASH_SIZE) { + // SHA-224 initial values + state_32[0] = 0xc1059ed8; + state_32[1] = 0x367cd507; + state_32[2] = 0x3070dd17; + state_32[3] = 0xf70e5939; + state_32[4] = 0xffc00b31; + state_32[5] = 0x68581511; + state_32[6] = 0x64f98fa7; + state_32[7] = 0xbefa4fa4; + } else { + // SHA-256 initial values + state_32[0] = 0x6a09e667; + state_32[1] = 0xbb67ae85; + state_32[2] = 0x3c6ef372; + state_32[3] = 0xa54ff53a; + state_32[4] = 0x510e527f; + state_32[5] = 0x9b05688c; + state_32[6] = 0x1f83d9ab; + state_32[7] = 0x5be0cd19; + } + } else { + // SHA-384/512 initial values + if (hash_size == SHA2_384_HASH_SIZE) { + // SHA-384 initial values + state_64[0] = 0xcbbb9d5dc1059ed8ULL; + state_64[1] = 0x629a292a367cd507ULL; + state_64[2] = 0x9159015a3070dd17ULL; + state_64[3] = 0x152fecd8f70e5939ULL; + state_64[4] = 0x67332667ffc00b31ULL; + state_64[5] = 0x8eb44a8768581511ULL; + state_64[6] = 0xdb0c2e0d64f98fa7ULL; + state_64[7] = 0x47b5481dbefa4fa4ULL; + } else { + // SHA-512 initial values + state_64[0] = 0x6a09e667f3bcc908ULL; + state_64[1] = 0xbb67ae8584caa73bULL; + state_64[2] = 0x3c6ef372fe94f82bULL; + state_64[3] = 0xa54ff53a5f1d36f1ULL; + state_64[4] = 0x510e527fade682d1ULL; + state_64[5] = 0x9b05688c2b3e6c1fULL; + state_64[6] = 0x1f83d9abfb41bd6bULL; + state_64[7] = 0x5be0cd19137e2179ULL; + } + } +} + +// Process a block for SHA-256 +void SHA2Builder::process_block_sha256(const uint8_t *data) { + uint32_t w[64]; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2; + + // Prepare message schedule + for (int i = 0; i < 16; i++) { + w[i] = BYTESWAP32(((uint32_t *)data)[i]); + } + for (int i = 16; i < 64; i++) { + w[i] = SIG1_32(w[i - 2]) + w[i - 7] + SIG0_32(w[i - 15]) + w[i - 16]; + } + + // Initialize working variables + a = state_32[0]; + b = state_32[1]; + c = state_32[2]; + d = state_32[3]; + e = state_32[4]; + f = state_32[5]; + g = state_32[6]; + h = state_32[7]; + + // Main loop + for (int i = 0; i < 64; i++) { + t1 = h + EP1_32(e) + CH32(e, f, g) + sha256_k[i] + w[i]; + t2 = EP0_32(a) + MAJ32(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Add the compressed chunk to the current hash value + state_32[0] += a; + state_32[1] += b; + state_32[2] += c; + state_32[3] += d; + state_32[4] += e; + state_32[5] += f; + state_32[6] += g; + state_32[7] += h; +} + +// Process a block for SHA-512 +void SHA2Builder::process_block_sha512(const uint8_t *data) { + uint64_t w[80]; + uint64_t a, b, c, d, e, f, g, h; + uint64_t t1, t2; + + // Prepare message schedule + for (int i = 0; i < 16; i++) { + w[i] = BYTESWAP64(((uint64_t *)data)[i]); + } + for (int i = 16; i < 80; i++) { + w[i] = SIG1_64(w[i - 2]) + w[i - 7] + SIG0_64(w[i - 15]) + w[i - 16]; + } + + // Initialize working variables + a = state_64[0]; + b = state_64[1]; + c = state_64[2]; + d = state_64[3]; + e = state_64[4]; + f = state_64[5]; + g = state_64[6]; + h = state_64[7]; + + // Main loop + for (int i = 0; i < 80; i++) { + t1 = h + EP1_64(e) + CH64(e, f, g) + sha512_k[i] + w[i]; + t2 = EP0_64(a) + MAJ64(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Add the compressed chunk to the current hash value + state_64[0] += a; + state_64[1] += b; + state_64[2] += c; + state_64[3] += d; + state_64[4] += e; + state_64[5] += f; + state_64[6] += g; + state_64[7] += h; +} + +// Add data to the hash computation +void SHA2Builder::add(const uint8_t *data, size_t len) { + if (finalized || len == 0) { + return; + } + + total_length += len; + size_t offset = 0; + + // Process any buffered data first + if (buffer_size > 0) { + size_t to_copy = std::min(len, block_size - buffer_size); + memcpy(buffer + buffer_size, data, to_copy); + buffer_size += to_copy; + offset += to_copy; + + if (buffer_size == block_size) { + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + buffer_size = 0; + } + } + + // Process full blocks + while (offset + block_size <= len) { + if (is_sha512) { + process_block_sha512(data + offset); + } else { + process_block_sha256(data + offset); + } + offset += block_size; + } + + // Buffer remaining data + if (offset < len) { + memcpy(buffer, data + offset, len - offset); + buffer_size = len - offset; + } +} + +// Add data from a stream +bool SHA2Builder::addStream(Stream &stream, const size_t maxLen) { + const int buf_size = 512; + int maxLengthLeft = maxLen; + uint8_t *buf = (uint8_t *)malloc(buf_size); + + if (!buf) { + return false; + } + + int bytesAvailable = stream.available(); + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) { + // Determine number of bytes to read + int readBytes = bytesAvailable; + if (readBytes > maxLengthLeft) { + readBytes = maxLengthLeft; + } + if (readBytes > buf_size) { + readBytes = buf_size; + } + + // Read data and check if we got something + int numBytesRead = stream.readBytes(buf, readBytes); + if (numBytesRead < 1) { + free(buf); + return false; + } + + // Update SHA2 with buffer payload + add(buf, numBytesRead); + + // Update available number of bytes + maxLengthLeft -= numBytesRead; + bytesAvailable = stream.available(); + } + free(buf); + return true; +} + +// Pad the input according to SHA2 specification +void SHA2Builder::pad() { + // Calculate the number of bytes we have + uint64_t bit_length = total_length * 8; + + // Add the bit '1' to the message + buffer[buffer_size++] = 0x80; + + // Pad with zeros until we have enough space for the length + while (buffer_size + 8 > block_size) { + if (buffer_size < block_size) { + buffer[buffer_size++] = 0x00; + } else { + // Process the block + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + buffer_size = 0; + } + } + + // Pad with zeros to make room for the length + while (buffer_size + 8 < block_size) { + buffer[buffer_size++] = 0x00; + } + + // Add the length in bits + if (is_sha512) { + // For SHA-512, length is 128 bits (16 bytes) + // We only use the lower 64 bits for now + for (int i = 0; i < 8; i++) { + buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8)); + } + // Set the upper 64 bits to 0 (for SHA-384/512, length is limited to 2^128-1) + for (int i = 0; i < 8; i++) { + buffer[block_size - 16 + i] = 0x00; + } + } else { + // For SHA-256, length is 64 bits (8 bytes) + for (int i = 0; i < 8; i++) { + buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8)); + } + } +} + +// Finalize the hash computation +void SHA2Builder::calculate() { + if (finalized) { + return; + } + + // Pad the input + pad(); + + // Process the final block + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + + // Extract bytes from the state + if (is_sha512) { + for (size_t i = 0; i < hash_size; i++) { + hash[i] = (uint8_t)(state_64[i >> 3] >> (56 - ((i & 0x7) << 3))); + } + } else { + for (size_t i = 0; i < hash_size; i++) { + hash[i] = (uint8_t)(state_32[i >> 2] >> (24 - ((i & 0x3) << 3))); + } + } + + finalized = true; +} + +// Get the hash as bytes +void SHA2Builder::getBytes(uint8_t *output) { + memcpy(output, hash, hash_size); +} + +// Get the hash as hex string +void SHA2Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA2 not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, hash_size * 2 + 1, hash, hash_size); +} + +// Get the hash as String +String SHA2Builder::toString() { + char out[(hash_size * 2) + 1]; + getChars(out); + return String(out); +} diff --git a/libraries/Hash/src/SHA2Builder.h b/libraries/Hash/src/SHA2Builder.h new file mode 100644 index 00000000000..998e624bcfc --- /dev/null +++ b/libraries/Hash/src/SHA2Builder.h @@ -0,0 +1,96 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SHA2Builder_h +#define SHA2Builder_h + +#include +#include + +#include "HashBuilder.h" + +// SHA2 constants +#define SHA2_224_HASH_SIZE 28 +#define SHA2_256_HASH_SIZE 32 +#define SHA2_384_HASH_SIZE 48 +#define SHA2_512_HASH_SIZE 64 + +#define SHA2_224_BLOCK_SIZE 64 +#define SHA2_256_BLOCK_SIZE 64 +#define SHA2_384_BLOCK_SIZE 128 +#define SHA2_512_BLOCK_SIZE 128 + +// SHA2 state sizes (in 32-bit words for SHA-224/256, 64-bit words for SHA-384/512) +#define SHA2_224_STATE_SIZE 8 +#define SHA2_256_STATE_SIZE 8 +#define SHA2_384_STATE_SIZE 8 +#define SHA2_512_STATE_SIZE 8 + +class SHA2Builder : public HashBuilder { +protected: + uint32_t state_32[8]; // SHA-224/256 state (256 bits) + uint64_t state_64[8]; // SHA-384/512 state (512 bits) + uint8_t buffer[128]; // Input buffer (max block size) + size_t block_size; // Block size + size_t hash_size; // Output hash size + size_t buffer_size; // Current buffer size + bool finalized; // Whether hash has been finalized + bool is_sha512; // Whether using SHA-512 family + uint8_t hash[64]; // Hash result + uint64_t total_length; // Total length of input data + + void process_block_sha256(const uint8_t *data); + void process_block_sha512(const uint8_t *data); + void pad(); + +public: + using HashBuilder::add; + + SHA2Builder(size_t hash_size = SHA2_256_HASH_SIZE); + virtual ~SHA2Builder() {} + + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + + size_t getHashSize() const override { + return hash_size; + } +}; + +class SHA224Builder : public SHA2Builder { +public: + SHA224Builder() : SHA2Builder(SHA2_224_HASH_SIZE) {} +}; + +class SHA256Builder : public SHA2Builder { +public: + SHA256Builder() : SHA2Builder(SHA2_256_HASH_SIZE) {} +}; + +class SHA384Builder : public SHA2Builder { +public: + SHA384Builder() : SHA2Builder(SHA2_384_HASH_SIZE) {} +}; + +class SHA512Builder : public SHA2Builder { +public: + SHA512Builder() : SHA2Builder(SHA2_512_HASH_SIZE) {} +}; + +#endif diff --git a/libraries/Hash/src/SHA3Builder.cpp b/libraries/Hash/src/SHA3Builder.cpp new file mode 100644 index 00000000000..6108aefe4c0 --- /dev/null +++ b/libraries/Hash/src/SHA3Builder.cpp @@ -0,0 +1,267 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "esp32-hal-log.h" +#include "SHA3Builder.h" + +// Keccak round constants +static const uint64_t keccak_round_constants[24] = {0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808AULL, 0x8000000080008000ULL, + 0x000000000000808BULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008AULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000AULL, + 0x000000008000808BULL, 0x800000000000008BULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800AULL, 0x800000008000000AULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL}; + +// Rho rotation constants +static const uint32_t rho[6] = {0x3f022425, 0x1c143a09, 0x2c3d3615, 0x27191713, 0x312b382e, 0x3e030832}; + +// Pi permutation constants +static const uint32_t pi[6] = {0x110b070a, 0x10050312, 0x04181508, 0x0d13170f, 0x0e14020c, 0x01060916}; + +// Macros for bit manipulation +#define ROTR64(x, y) (((x) << (64U - (y))) | ((x) >> (y))) + +// Keccak-f permutation +void SHA3Builder::keccak_f(uint64_t state[25]) { + uint64_t lane[5]; + uint64_t *s = state; + int i; + + for (int round = 0; round < 24; round++) { + uint64_t t; + + // Theta step + for (i = 0; i < 5; i++) { + lane[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]; + } + for (i = 0; i < 5; i++) { + t = lane[(i + 4) % 5] ^ ROTR64(lane[(i + 1) % 5], 63); + s[i] ^= t; + s[i + 5] ^= t; + s[i + 10] ^= t; + s[i + 15] ^= t; + s[i + 20] ^= t; + } + + // Rho step + for (i = 1; i < 25; i += 4) { + uint32_t r = rho[(i - 1) >> 2]; + for (int j = i; j < i + 4; j++) { + uint8_t r8 = (uint8_t)(r >> 24); + r <<= 8; + s[j] = ROTR64(s[j], r8); + } + } + + // Pi step + t = s[1]; + for (i = 0; i < 24; i += 4) { + uint32_t p = pi[i >> 2]; + for (unsigned j = 0; j < 4; j++) { + uint64_t tmp = s[p & 0xff]; + s[p & 0xff] = t; + t = tmp; + p >>= 8; + } + } + + // Chi step + for (i = 0; i <= 20; i += 5) { + lane[0] = s[i]; + lane[1] = s[i + 1]; + lane[2] = s[i + 2]; + lane[3] = s[i + 3]; + lane[4] = s[i + 4]; + s[i + 0] ^= (~lane[1]) & lane[2]; + s[i + 1] ^= (~lane[2]) & lane[3]; + s[i + 2] ^= (~lane[3]) & lane[4]; + s[i + 3] ^= (~lane[4]) & lane[0]; + s[i + 4] ^= (~lane[0]) & lane[1]; + } + + // Iota step + s[0] ^= keccak_round_constants[round]; + } +} + +// Process a block of data +void SHA3Builder::process_block(const uint8_t *data) { + // XOR the data into the state using byte-level operations + for (size_t i = 0; i < rate; i++) { + size_t state_idx = i >> 3; // i / 8 + size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8 + uint64_t byte_val = (uint64_t)data[i] << bit_offset; + state[state_idx] ^= byte_val; + } + + // Apply Keccak-f permutation + keccak_f(state); +} + +// Pad the input according to SHA3 specification +void SHA3Builder::pad() { + // Clear the buffer first + memset(buffer + buffer_size, 0, rate - buffer_size); + + // Add the domain separator (0x06) at the current position + buffer[buffer_size] = 0x06; + + // Set the last byte to indicate the end (0x80) + buffer[rate - 1] = 0x80; +} + +// Constructor +SHA3Builder::SHA3Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false) { + // Calculate rate based on hash size + if (hash_size == SHA3_224_HASH_SIZE) { + rate = SHA3_224_RATE; + } else if (hash_size == SHA3_256_HASH_SIZE) { + rate = SHA3_256_RATE; + } else if (hash_size == SHA3_384_HASH_SIZE) { + rate = SHA3_384_RATE; + } else if (hash_size == SHA3_512_HASH_SIZE) { + rate = SHA3_512_RATE; + } else { + log_e("Invalid hash size: %d", hash_size); + rate = 0; // Invalid hash size + } +} + +// Initialize the hash computation +void SHA3Builder::begin() { + // Clear the state + memset(state, 0, sizeof(state)); + memset(buffer, 0, sizeof(buffer)); + buffer_size = 0; + finalized = false; +} + +// Add data to the hash computation +void SHA3Builder::add(const uint8_t *data, size_t len) { + if (finalized || len == 0) { + return; + } + + size_t offset = 0; + + // Process any buffered data first + if (buffer_size > 0) { + size_t to_copy = std::min(len, rate - buffer_size); + memcpy(buffer + buffer_size, data, to_copy); + buffer_size += to_copy; + offset += to_copy; + + if (buffer_size == rate) { + process_block(buffer); + buffer_size = 0; + } + } + + // Process full blocks + while (offset + rate <= len) { + process_block(data + offset); + offset += rate; + } + + // Buffer remaining data + if (offset < len) { + memcpy(buffer, data + offset, len - offset); + buffer_size = len - offset; + } +} + +// Add data from a stream +bool SHA3Builder::addStream(Stream &stream, const size_t maxLen) { + const int buf_size = 512; + int maxLengthLeft = maxLen; + uint8_t *buf = (uint8_t *)malloc(buf_size); + + if (!buf) { + return false; + } + + int bytesAvailable = stream.available(); + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) { + // Determine number of bytes to read + int readBytes = bytesAvailable; + if (readBytes > maxLengthLeft) { + readBytes = maxLengthLeft; + } + if (readBytes > buf_size) { + readBytes = buf_size; + } + + // Read data and check if we got something + int numBytesRead = stream.readBytes(buf, readBytes); + if (numBytesRead < 1) { + free(buf); + return false; + } + + // Update SHA3 with buffer payload + add(buf, numBytesRead); + + // Update available number of bytes + maxLengthLeft -= numBytesRead; + bytesAvailable = stream.available(); + } + free(buf); + return true; +} + +// Finalize the hash computation +void SHA3Builder::calculate() { + if (finalized) { + return; + } + + // Pad the input + pad(); + + // Process the final block + process_block(buffer); + + // Extract bytes from the state + for (size_t i = 0; i < hash_size; i++) { + size_t state_idx = i >> 3; // i / 8 + size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8 + hash[i] = (uint8_t)(state[state_idx] >> bit_offset); + } + + finalized = true; +} + +// Get the hash as bytes +void SHA3Builder::getBytes(uint8_t *output) { + memcpy(output, hash, hash_size); +} + +// Get the hash as hex string +void SHA3Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA3 not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, hash_size * 2 + 1, hash, hash_size); +} + +// Get the hash as String +String SHA3Builder::toString() { + char out[(hash_size * 2) + 1]; + getChars(out); + return String(out); +} diff --git a/libraries/Hash/src/SHA3Builder.h b/libraries/Hash/src/SHA3Builder.h new file mode 100644 index 00000000000..64663fb2683 --- /dev/null +++ b/libraries/Hash/src/SHA3Builder.h @@ -0,0 +1,89 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SHA3Builder_h +#define SHA3Builder_h + +#include +#include + +#include "HashBuilder.h" + +// SHA3 constants +#define SHA3_224_HASH_SIZE 28 +#define SHA3_256_HASH_SIZE 32 +#define SHA3_384_HASH_SIZE 48 +#define SHA3_512_HASH_SIZE 64 + +#define SHA3_224_RATE 144 +#define SHA3_256_RATE 136 +#define SHA3_384_RATE 104 +#define SHA3_512_RATE 72 + +#define SHA3_STATE_SIZE 200 // 1600 bits = 200 bytes + +class SHA3Builder : public HashBuilder { +protected: + uint64_t state[25]; // SHA3 state (1600 bits) + uint8_t buffer[200]; // Input buffer + size_t rate; // Rate (block size) + size_t hash_size; // Output hash size + size_t buffer_size; // Current buffer size + bool finalized; // Whether hash has been finalized + uint8_t hash[64]; // Hash result + + void keccak_f(uint64_t state[25]); + void process_block(const uint8_t *data); + void pad(); + +public: + using HashBuilder::add; + + SHA3Builder(size_t hash_size = SHA3_256_HASH_SIZE); + virtual ~SHA3Builder() {} + + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + + size_t getHashSize() const override { + return hash_size; + } +}; + +class SHA3_224Builder : public SHA3Builder { +public: + SHA3_224Builder() : SHA3Builder(SHA3_224_HASH_SIZE) {} +}; + +class SHA3_256Builder : public SHA3Builder { +public: + SHA3_256Builder() : SHA3Builder(SHA3_256_HASH_SIZE) {} +}; + +class SHA3_384Builder : public SHA3Builder { +public: + SHA3_384Builder() : SHA3Builder(SHA3_384_HASH_SIZE) {} +}; + +class SHA3_512Builder : public SHA3Builder { +public: + SHA3_512Builder() : SHA3Builder(SHA3_512_HASH_SIZE) {} +}; + +#endif diff --git a/libraries/Insights/examples/DiagnosticsSmokeTest/ci.json b/libraries/Insights/examples/DiagnosticsSmokeTest/ci.json deleted file mode 100644 index cbd69d50029..00000000000 --- a/libraries/Insights/examples/DiagnosticsSmokeTest/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "requires": [ - "CONFIG_ESP_INSIGHTS_ENABLED=y" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Insights/examples/DiagnosticsSmokeTest/ci.yml b/libraries/Insights/examples/DiagnosticsSmokeTest/ci.yml new file mode 100644 index 00000000000..427f0f5ff51 --- /dev/null +++ b/libraries/Insights/examples/DiagnosticsSmokeTest/ci.yml @@ -0,0 +1,6 @@ +requires: + - CONFIG_ESP_INSIGHTS_ENABLED=y + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Insights/examples/MinimalDiagnostics/ci.json b/libraries/Insights/examples/MinimalDiagnostics/ci.json deleted file mode 100644 index cbd69d50029..00000000000 --- a/libraries/Insights/examples/MinimalDiagnostics/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "requires": [ - "CONFIG_ESP_INSIGHTS_ENABLED=y" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Insights/examples/MinimalDiagnostics/ci.yml b/libraries/Insights/examples/MinimalDiagnostics/ci.yml new file mode 100644 index 00000000000..427f0f5ff51 --- /dev/null +++ b/libraries/Insights/examples/MinimalDiagnostics/ci.yml @@ -0,0 +1,6 @@ +requires: + - CONFIG_ESP_INSIGHTS_ENABLED=y + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Insights/library.properties b/libraries/Insights/library.properties index 3ef98d25be6..27285e999fe 100644 --- a/libraries/Insights/library.properties +++ b/libraries/Insights/library.properties @@ -1,5 +1,5 @@ name=ESP Insights -version=3.3.0 +version=3.3.4 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=ESP Insights diff --git a/libraries/Insights/src/Insights.h b/libraries/Insights/src/Insights.h index 2e09818be94..0515d9df5da 100644 --- a/libraries/Insights/src/Insights.h +++ b/libraries/Insights/src/Insights.h @@ -101,7 +101,9 @@ class ESPInsightsClass { bool event(const char *tag, const char *format, ...); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_INSIGHTS) extern ESPInsightsClass Insights; +#endif extern "C" { #endif diff --git a/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino b/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino index f169ac24954..003e10638de 100644 --- a/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino +++ b/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino @@ -40,10 +40,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -54,10 +55,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/LittleFS/examples/LITTLEFS_time/ci.json b/libraries/LittleFS/examples/LITTLEFS_time/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/LittleFS/examples/LITTLEFS_time/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/LittleFS/examples/LITTLEFS_time/ci.yml b/libraries/LittleFS/examples/LITTLEFS_time/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/LittleFS/examples/LITTLEFS_time/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/LittleFS/library.properties b/libraries/LittleFS/library.properties index 202d8ad4a6d..f5ad750a315 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,5 +1,5 @@ name=LittleFS -version=3.3.0 +version=3.3.4 author= maintainer= sentence=LittleFS for esp32 diff --git a/libraries/LittleFS/src/LittleFS.cpp b/libraries/LittleFS/src/LittleFS.cpp index 761d1ba4c24..0d256a8283b 100644 --- a/libraries/LittleFS/src/LittleFS.cpp +++ b/libraries/LittleFS/src/LittleFS.cpp @@ -44,6 +44,7 @@ LittleFSFS::~LittleFSFS() { } bool LittleFSFS::begin(bool formatOnFail, const char *basePath, uint8_t maxOpenFiles, const char *partitionLabel) { + (void)maxOpenFiles; if (partitionLabel_) { free(partitionLabel_); @@ -95,11 +96,13 @@ void LittleFSFS::end() { } bool LittleFSFS::format() { + esp_log_level_set("*", ESP_LOG_NONE); bool wdt_active = disableCore0WDT(); esp_err_t err = esp_littlefs_format(partitionLabel_); if (wdt_active) { enableCore0WDT(); } + esp_log_level_set("*", (esp_log_level_t)CONFIG_LOG_DEFAULT_LEVEL); if (err) { log_e("Formatting LittleFS failed! Error: %d", err); return false; diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index da4ab7d1f6f..bf32bb442d0 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -38,7 +38,9 @@ class LittleFSFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) extern fs::LittleFSFS LittleFS; +#endif #endif /* CONFIG_LITTLEFS_PAGE_SIZE */ #endif diff --git a/libraries/Matter/examples/MatterColorLight/README.md b/libraries/Matter/examples/MatterColorLight/README.md new file mode 100644 index 00000000000..4d9e39e4f75 --- /dev/null +++ b/libraries/Matter/examples/MatterColorLight/README.md @@ -0,0 +1,170 @@ +# Matter Color Light Example + +This example demonstrates how to create a Matter-compatible color light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and manual control using a physical button. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | RGB LED | Status | +| --- | ---- | ------ | ----------------- | ------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a color light device +- Support for both Wi-Fi and Thread(*) connectivity +- RGB color control with HSV color model +- State persistence using `Preferences` library +- Button control for toggling light and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- RGB LED connected to GPIO pins (or using built-in RGB LED) +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **RGB LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in RGB LED): + ``` cpp + const uint8_t ledPin = 2; // Set your RGB LED pin here + ``` +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Light On/Off manual control. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = 0; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterColorLight.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: ON | RGB Color: (0,0,255) +Matter Node is commissioned and connected to the network. Ready for use. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle light on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a color light in your Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup + +## Code Structure + +The MatterColorLight example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter endpoint, restores the last known state from `Preferences`, and registers callbacks for state changes. +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the light and factory reset, and allows the Matter stack to process events. +3. **Callbacks**: + - `setLightState()`: Controls the physical RGB LED. + - `onChangeOnOff()`: Handles on/off state changes. + - `onChangeColorHSV()`: Handles color changes. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **RGB LED not responding**: Verify pin configurations and connections +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Color Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_color_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterColorLight/ci.json b/libraries/Matter/examples/MatterColorLight/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterColorLight/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterColorLight/ci.yml b/libraries/Matter/examples/MatterColorLight/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterColorLight/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterCommissionTest/MatterCommissionTest.ino b/libraries/Matter/examples/MatterCommissionTest/MatterCommissionTest.ino index aa593758548..81e7e6d45f8 100644 --- a/libraries/Matter/examples/MatterCommissionTest/MatterCommissionTest.ino +++ b/libraries/Matter/examples/MatterCommissionTest/MatterCommissionTest.ino @@ -77,5 +77,5 @@ void loop() { Serial.println("====> Decommissioning in 30 seconds. <===="); delay(30000); Matter.decommission(); - Serial.println("Matter Node is decommissioned. Commsssioning widget shall start over."); + Serial.println("Matter Node is decommissioned. Commissioning widget shall start over."); } diff --git a/libraries/Matter/examples/MatterCommissionTest/README.md b/libraries/Matter/examples/MatterCommissionTest/README.md new file mode 100644 index 00000000000..db5d0e158ea --- /dev/null +++ b/libraries/Matter/examples/MatterCommissionTest/README.md @@ -0,0 +1,155 @@ +# Matter Commission Test Example + +This example demonstrates how to test Matter commissioning functionality using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device connection to smart home ecosystems, and automatic decommissioning after a 30-second delay for continuous testing cycles. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an on/off light device +- Support for both Wi-Fi and Thread(*) connectivity +- Matter commissioning via QR code or manual pairing code +- Automatic decommissioning after 30 seconds for continuous testing +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +- Simple test tool for validating Matter commissioning workflows +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +## Building and Flashing + +1. Open the `MatterCommissionTest.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Fabric not commissioned yet. Waiting for commissioning. +Matter Fabric not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. +====> Decommissioning in 30 seconds. <==== +Matter Node is decommissioned. Commissioning widget shall start over. + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +... +``` + +## Using the Device + +### Test Cycle + +The device operates in a continuous test cycle: + +1. **Commissioning Phase**: The device waits for Matter commissioning. It displays the manual pairing code and QR code URL in the Serial Monitor. +2. **Commissioned Phase**: Once commissioned, the device is connected to the Matter network and ready for use. +3. **Automatic Decommissioning**: After 30 seconds, the device automatically decommissions itself. +4. **Repeat**: The cycle repeats, allowing you to test the commissioning process multiple times. + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device during each test cycle. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an on/off light in your Home app +7. After 30 seconds, the device will automatically decommission and the cycle will repeat + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app +6. After 30 seconds, the device will automatically decommission and the cycle will repeat + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. After 30 seconds, the device will automatically decommission and the cycle will repeat + +## Code Structure + +The MatterCommissionTest example consists of the following main components: + +1. **`setup()`**: Configures Wi-Fi (if needed), initializes the Matter On/Off Light endpoint, and starts the Matter stack. +2. **`loop()`**: Checks the Matter commissioning state, displays pairing information when not commissioned, waits for commissioning, and then automatically decommissions after 30 seconds to repeat the cycle. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Failed to commission**: Try waiting for the next cycle after decommissioning. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **Device keeps decommissioning**: This is expected behavior - the device automatically decommissions after 30 seconds to allow continuous testing + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterCommissionTest/ci.json b/libraries/Matter/examples/MatterCommissionTest/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterCommissionTest/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterCommissionTest/ci.yml b/libraries/Matter/examples/MatterCommissionTest/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterCommissionTest/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterComposedLights/README.md b/libraries/Matter/examples/MatterComposedLights/README.md new file mode 100644 index 00000000000..96446810f9f --- /dev/null +++ b/libraries/Matter/examples/MatterComposedLights/README.md @@ -0,0 +1,183 @@ +# Matter Composed Lights Example + +This example demonstrates how to create a Matter node with multiple light endpoints using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, a single Matter node containing three different light types (On/Off Light, Dimmable Light, and Color Light), and factory reset using a physical button. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a composed device with multiple light endpoints +- Three light endpoints in a single Matter node: + - Light #1: Simple On/Off Light + - Light #2: Dimmable Light (on/off with brightness control) + - Light #3: Color Light (RGB color control) +- Support for both Wi-Fi and Thread(*) connectivity +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +- Periodic state display of all three lights +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default for factory reset (long press >5 seconds) + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterComposedLights.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +====================== +Matter Light #1 is OFF +Matter Light #2 is OFF +Matter Light #3 is OFF +====================== +Light1 changed state to: ON +Light2 changed state to: ON +Light3 changed state to: ON +====================== +Matter Light #1 is ON +Matter Light #2 is ON +Matter Light #3 is ON +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. After commissioning, you will see three separate light devices in your smart home app: + +- **Light #1**: Simple on/off light +- **Light #2**: Dimmable light with brightness control +- **Light #3**: Color light with RGB color control + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as three separate lights in your Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The three lights will appear in your Alexa app + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The three lights will appear in your Google Home app + +## Code Structure + +The MatterComposedLights example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up three Matter endpoints (OnOffLight, DimmableLight, ColorLight), and registers callbacks for state changes. +2. **`loop()`**: Checks the Matter commissioning state, displays the state of all three lights every 5 seconds, handles button input for factory reset, and allows the Matter stack to process events. +3. **Callbacks**: + - `setLightOnOff1()`: Handles on/off state changes for Light #1. + - `setLightOnOff2()`: Handles on/off state changes for Light #2. + - `setLightOnOff3()`: Handles on/off state changes for Light #3. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Only one or two lights appear**: Some smart home platforms may group or display lights differently. Check your app's device list +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter On/Off Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_on_off_light.html) +- [Matter Dimmable Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_dimmable_light.html) +- [Matter Color Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_color_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterComposedLights/ci.json b/libraries/Matter/examples/MatterComposedLights/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterComposedLights/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterComposedLights/ci.yml b/libraries/Matter/examples/MatterComposedLights/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterComposedLights/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterContactSensor/README.md b/libraries/Matter/examples/MatterContactSensor/README.md new file mode 100644 index 00000000000..0f54a8f0010 --- /dev/null +++ b/libraries/Matter/examples/MatterContactSensor/README.md @@ -0,0 +1,178 @@ +# Matter Contact Sensor Example + +This example demonstrates how to create a Matter-compatible contact sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, manual control using a physical button, and automatic simulation of contact state changes. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a contact sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Contact state indication using LED (ON = Closed, OFF = Open) +- Automatic simulation of contact state changes every 20 seconds +- Button control for toggling contact state and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pins (or using built-in LED) to indicate contact state +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Contact Sensor state toggle and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterContactSensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +User button released. Setting the Contact Sensor to Closed. +User button released. Setting the Contact Sensor to Open. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle contact sensor state (Open/Closed) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Automatic Simulation + +The contact sensor state automatically toggles every 20 seconds to simulate a real contact sensor (such as a door or window sensor). The LED will reflect the current state: +- **LED ON**: Contact sensor is Closed +- **LED OFF**: Contact sensor is Open + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a contact sensor in your Home app +7. You can monitor the contact state (Open/Closed) and receive notifications when the state changes + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The contact sensor will appear in your Alexa app +6. You can monitor the contact state and set up routines based on state changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The contact sensor will appear in your Google Home app + +## Code Structure + +The MatterContactSensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter Contact Sensor endpoint with initial state (Open), and waits for Matter commissioning. +2. **`loop()`**: Handles button input for toggling contact state and factory reset, and automatically simulates contact state changes every 20 seconds. +3. **`simulatedHWContactSensor()`**: Simulates a hardware contact sensor by toggling the contact state every 20 seconds. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **Contact sensor state not updating**: Check Serial Monitor output to verify state changes are being processed +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Contact Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_contact_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterContactSensor/ci.json b/libraries/Matter/examples/MatterContactSensor/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterContactSensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterContactSensor/ci.yml b/libraries/Matter/examples/MatterContactSensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterContactSensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterDimmableLight/README.md b/libraries/Matter/examples/MatterDimmableLight/README.md new file mode 100644 index 00000000000..f1381b2641c --- /dev/null +++ b/libraries/Matter/examples/MatterDimmableLight/README.md @@ -0,0 +1,176 @@ +# Matter Dimmable Light Example + +This example demonstrates how to create a Matter-compatible dimmable light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and manual control using a physical button. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a dimmable light device +- Support for both Wi-Fi and Thread(*) connectivity +- Brightness control (0-255 levels) +- State persistence using `Preferences` library +- Button control for toggling light and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pins (or using built-in LED/RGB LED) +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 (supports both RGB LED and regular LED with PWM brightness control) +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Light On/Off manual control. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterDimmableLight.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: ON | brightness: 15 +Matter Node is commissioned and connected to the network. Ready for use. +Light OnOff changed to ON +Light Brightness changed to 128 +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle light on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a dimmable light in your Home app +7. You can control both the on/off state and brightness level (0-100%) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The dimmable light will appear in your Alexa app +6. You can control brightness using voice commands like "Alexa, set light to 50 percent" + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control brightness using voice commands or the slider in the app + +## Code Structure + +The MatterDimmableLight example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter endpoint, restores the last known state (on/off and brightness) from `Preferences`, and registers callbacks for state changes. +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the light and factory reset, and allows the Matter stack to process events. +3. **Callbacks**: + - `setLightState()`: Controls the physical LED with brightness level (supports both RGB LED and regular LED with PWM). + - `onChangeOnOff()`: Handles on/off state changes. + - `onChangeBrightness()`: Handles brightness level changes (0-255). + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding or brightness not working**: Verify pin configurations and connections. For non-RGB LEDs, ensure the pin supports PWM (analogWrite) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Dimmable Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_dimmable_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterDimmableLight/ci.json b/libraries/Matter/examples/MatterDimmableLight/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterDimmableLight/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterDimmableLight/ci.yml b/libraries/Matter/examples/MatterDimmableLight/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterDimmableLight/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterDimmablePlugin/MatterDimmablePlugin.ino b/libraries/Matter/examples/MatterDimmablePlugin/MatterDimmablePlugin.ino new file mode 100644 index 00000000000..2cbebc5b742 --- /dev/null +++ b/libraries/Matter/examples/MatterDimmablePlugin/MatterDimmablePlugin.ino @@ -0,0 +1,186 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif +#include + +// List of Matter Endpoints for this Node +// Dimmable Plugin Endpoint +MatterDimmablePlugin DimmablePlugin; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// it will keep last OnOff & Level state stored, using Preferences +Preferences matterPref; +const char *onOffPrefKey = "OnOff"; +const char *levelPrefKey = "Level"; + +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t pluginPin = RGB_BUILTIN; // Using built-in RGB LED for visualization +#else +const uint8_t pluginPin = 2; // Set your pin here if your board has not defined RGB_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debounceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Set the RGB LED Plugin output based on the current state and level +bool setPluginState(bool state, uint8_t level) { + Serial.printf("User Callback :: New Plugin State = %s, Level = %d\r\n", state ? "ON" : "OFF", level); + if (state) { + // Plugin is ON - set RGB LED level (0-255 maps to 0-100% power) +#ifdef RGB_BUILTIN + rgbLedWrite(pluginPin, level, level, level); +#else + analogWrite(pluginPin, level); +#endif + } else { + // Plugin is OFF - turn off output +#ifndef RGB_BUILTIN + // After analogWrite(), it is necessary to set the GPIO to digital mode first + pinMode(pluginPin, OUTPUT); +#endif + digitalWrite(pluginPin, LOW); + } + // store last Level and OnOff state for when the Plugin is restarted / power goes off + matterPref.putUChar(levelPrefKey, level); + matterPref.putBool(onOffPrefKey, state); + // This callback must return the success state to Matter core + return true; +} + +void setup() { + // Initialize the USER BUTTON (Boot button) GPIO that will act as a toggle switch + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the RGB LED (plugin) GPIO + pinMode(pluginPin, OUTPUT); + digitalWrite(pluginPin, LOW); // Start with plugin off + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // We start by connecting to a WiFi network + Serial.print("Connecting to "); + Serial.println(ssid); + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println("\r\nWiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + delay(500); +#endif + + // Initialize Matter EndPoint + matterPref.begin("MatterPrefs", false); + // default OnOff state is OFF if not stored before + bool lastOnOffState = matterPref.getBool(onOffPrefKey, false); + // default level ~= 25% (64/255) + uint8_t lastLevel = matterPref.getUChar(levelPrefKey, 64); + DimmablePlugin.begin(lastOnOffState, lastLevel); + // set the callback function to handle the Plugin state change + DimmablePlugin.onChange(setPluginState); + + // lambda functions are used to set the attribute change callbacks + DimmablePlugin.onChangeOnOff([](bool state) { + Serial.printf("Plugin OnOff changed to %s\r\n", state ? "ON" : "OFF"); + return true; + }); + DimmablePlugin.onChangeLevel([](uint8_t level) { + Serial.printf("Plugin Level changed to %d\r\n", level); + return true; + }); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + // This may be a restart of a already commissioned Matter accessory + if (Matter.isDeviceCommissioned()) { + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + Serial.printf("Initial state: %s | level: %d\r\n", DimmablePlugin ? "ON" : "OFF", DimmablePlugin.getLevel()); + // configure the Plugin based on initial on-off state and level + DimmablePlugin.updateAccessory(); + } +} + +void loop() { + // Check Matter Plugin Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Plugin Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.printf("Initial state: %s | level: %d\r\n", DimmablePlugin ? "ON" : "OFF", DimmablePlugin.getLevel()); + // configure the Plugin based on initial on-off state and level + DimmablePlugin.updateAccessory(); + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } + + // A button is also used to control the plugin + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + // Onboard User Button is used as a Plugin toggle switch or to decommission it + uint32_t time_diff = millis() - button_time_stamp; + if (digitalRead(buttonPin) == HIGH && button_state && time_diff > debounceTime) { + // Toggle button is released - toggle the plugin + Serial.println("User button released. Toggling Plugin!"); + DimmablePlugin.toggle(); // Matter Controller also can see the change + button_state = false; // released + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning the Plugin Matter Accessory. It shall be commissioned again."); + DimmablePlugin = false; // turn the plugin off + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so + } +} diff --git a/libraries/Matter/examples/MatterDimmablePlugin/README.md b/libraries/Matter/examples/MatterDimmablePlugin/README.md new file mode 100644 index 00000000000..d823c5c1ca7 --- /dev/null +++ b/libraries/Matter/examples/MatterDimmablePlugin/README.md @@ -0,0 +1,222 @@ +# Matter Dimmable Plugin Example + +This example demonstrates how to create a Matter-compatible dimmable plugin unit (power outlet with level control) device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and state persistence for dimmable power control applications. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Relay/Dimmer | Status | +| --- | ---- | ------ | ----------------- | ------------ | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a dimmable plugin unit (power outlet with level control) device +- Support for both Wi-Fi and Thread(*) connectivity +- On/off control and power level control (0-255 levels) +- State persistence using `Preferences` library +- Button control for toggling plugin and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- Power relay/dimmer module or RGB LED for visualization (for testing, uses built-in RGB LED if available) +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **RGB LED/Relay/Dimmer Pin**: Uses `RGB_BUILTIN` if defined (for testing with RGB LED visualization), otherwise pin 2. For production use, connect this to a PWM-capable pin for dimmer control or relay control pin. +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Power relay/dimmer pin configuration** (if not using built-in LED): + For production use, change this to a PWM-capable GPIO pin connected to your dimmer control module: + ```cpp + const uint8_t pluginPin = 2; // Set your PWM-capable pin here for dimmer control + ``` + + **Note**: The example uses `RGB_BUILTIN` if available on your board (e.g., ESP32-S3, ESP32-C3) to visually demonstrate the level control. The RGB LED brightness will change based on the power level (0-255). For boards without RGB LED, it falls back to a regular pin with PWM support. + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Plugin On/Off manual control. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterDimmablePlugin.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: OFF | level: 64 +Matter Node is commissioned and connected to the network. Ready for use. +Plugin OnOff changed to ON +Plugin Level changed to 128 +User Callback :: New Plugin State = ON, Level = 128 +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle plugin on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### State Persistence + +The device saves the last known on/off state and power level using the `Preferences` library. After a power cycle or restart: + +- The device will restore to the last saved state (ON or OFF) and power level +- Default state is OFF with level 64 (25%) if no previous state was saved +- The Matter controller will be notified of the restored state +- The relay/dimmer will reflect the restored state and level + +### Power Relay/Dimmer Integration + +For production use with a power relay or dimmer module: + +1. **For Dimmer Control (PWM-based)**: + - Connect the dimmer module to your ESP32: + - Dimmer VCC → ESP32 3.3 V or 5 V (check dimmer module specifications) + - Dimmer GND → ESP32 GND + - Dimmer PWM/Control → ESP32 GPIO pin with PWM support (configured as `pluginPin`) + - Update the pin configuration in the sketch: + ```cpp + const uint8_t pluginPin = 2; // Your PWM-capable pin for dimmer control + ``` + - The level (0-255) will control the dimmer output power (0% to 100%) + +2. **For Relay Control (On/Off only)**: + - Connect the relay module to your ESP32: + - Relay VCC → ESP32 3.3 V or 5 V (check relay module specifications) + - Relay GND → ESP32 GND + - Relay IN → ESP32 GPIO pin (configured as `pluginPin`) + - Update the pin configuration in the sketch: + ```cpp + const uint8_t pluginPin = 2; // Your relay control pin + ``` + - Note: When using a relay, the level control will still work but the relay will only switch on/off based on the state + +3. **Test the relay/dimmer** by controlling it via Matter app - the device should respond to both on/off and level changes + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a dimmable outlet/switch in your Home app +7. You can control both the on/off state and power level (0-100%) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The dimmable plugin will appear in your Alexa app +6. You can control power level using voice commands like "Alexa, set outlet to 50 percent" + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control power level using voice commands or the slider in the app + +## Code Structure + +The MatterDimmablePlugin example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, relay/dimmer pin), configures Wi-Fi (if needed), initializes `Preferences` library, sets up the Matter plugin endpoint with the last saved state (defaults to OFF with level 64 if not previously saved), registers callback functions, and starts the Matter stack. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the plugin and factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + - `setPluginState()`: Controls the physical relay/dimmer based on the on/off state and power level, saves the state to `Preferences` for persistence, and prints the state change to Serial Monitor. + - `onChangeOnOff()`: Handles on/off state changes. + - `onChangeLevel()`: Handles power level changes (0-255). + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Relay/Dimmer not responding**: Verify pin configurations and connections. For dimmer modules, ensure the pin supports PWM (analogWrite). For relay modules, ensure proper power supply and wiring +- **Level control not working**: For dimmer control, verify the pin supports PWM. Check that `analogWrite()` or `rgbLedWrite()` (for RGB LED) is working correctly on your board. On boards with RGB LED, the brightness will change based on the level value (0-255) +- **State not persisting**: Check that the `Preferences` library is properly initialized and that flash memory is not corrupted +- **Relay not switching**: For relay modules, verify the control signal voltage levels match your relay module requirements (some relays need 5 V, others work with 3.3 V) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Dimmable Plugin Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_dimmable_plugin.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterDimmablePlugin/ci.yml b/libraries/Matter/examples/MatterDimmablePlugin/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterDimmablePlugin/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterEnhancedColorLight/README.md b/libraries/Matter/examples/MatterEnhancedColorLight/README.md new file mode 100644 index 00000000000..100c216cedf --- /dev/null +++ b/libraries/Matter/examples/MatterEnhancedColorLight/README.md @@ -0,0 +1,183 @@ +# Matter Enhanced Color Light Example + +This example demonstrates how to create a Matter-compatible enhanced color light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and manual control using a physical button. The enhanced color light provides additional features including color temperature control and brightness adjustment. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | RGB LED | Status | +| --- | ---- | ------ | ----------------- | ------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an enhanced color light device +- Support for both Wi-Fi and Thread(*) connectivity +- RGB color control with HSV color model +- Color temperature control (warm to cool white) +- Brightness control (0-255 levels) +- State persistence using `Preferences` library +- Button control for toggling light and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- RGB LED connected to GPIO pins (or using built-in RGB LED) +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **RGB LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in RGB LED): + ```cpp + const uint8_t ledPin = 2; // Set your RGB LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Light On/Off manual control. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterEnhancedColorLight.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: ON | RGB Color: (255,255,255) +Matter Node is commissioned and connected to the network. Ready for use. +Light OnOff changed to ON +Light Color Temperature changed to 370 +Light brightness changed to 128 +Light HSV Color changed to (120,255,255) +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle light on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an enhanced color light in your Home app +7. You can control RGB color, color temperature (warm/cool white), and brightness + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The enhanced color light will appear in your Alexa app +6. You can control color, color temperature, and brightness using voice commands or the app + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control color, color temperature, and brightness using voice commands or the app controls + +## Code Structure + +The MatterEnhancedColorLight example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter endpoint, restores the last known state (on/off and HSV color) from `Preferences`, and registers callbacks for state changes. +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the light and factory reset, and allows the Matter stack to process events. +3. **Callbacks**: + - `setLightState()`: Controls the physical RGB LED with state, HSV color, brightness, and color temperature parameters. + - `onChangeOnOff()`: Handles on/off state changes. + - `onChangeColorHSV()`: Handles HSV color changes (Hue and Saturation). + - `onChangeBrightness()`: Handles brightness level changes (0-255, maps to HSV Value). + - `onChangeColorTemperature()`: Handles color temperature changes (warm to cool white). + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **RGB LED not responding**: Verify pin configurations and connections +- **Color temperature not working**: Verify that the color temperature callback is properly handling HSV conversion +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Enhanced Color Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_enhanced_color_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterEnhancedColorLight/ci.json b/libraries/Matter/examples/MatterEnhancedColorLight/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterEnhancedColorLight/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterEnhancedColorLight/ci.yml b/libraries/Matter/examples/MatterEnhancedColorLight/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterEnhancedColorLight/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterEvents/README.md b/libraries/Matter/examples/MatterEvents/README.md new file mode 100644 index 00000000000..985fb8748e5 --- /dev/null +++ b/libraries/Matter/examples/MatterEvents/README.md @@ -0,0 +1,185 @@ +# Matter Events Example + +This example demonstrates how to monitor and handle Matter events using an ESP32 SoC microcontroller.\ +The application showcases Matter event handling, commissioning, and automatic decommissioning. It provides a comprehensive view of all Matter events that occur during device operation, making it useful for debugging and understanding the Matter protocol behavior. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation with comprehensive event monitoring +- Support for both Wi-Fi and Thread(*) connectivity +- Event callback handler that displays all Matter events to Serial Monitor +- Monitors connectivity changes (Wi-Fi, Thread, Internet, IPv4/IPv6) +- Tracks commissioning lifecycle events +- Monitors fabric management events +- Tracks BLE/CHIPoBLE events +- Automatic decommissioning after 60 seconds for continuous testing +- Matter commissioning via QR code or manual pairing code +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +## Building and Flashing + +1. Open the `MatterEvents.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning and displays Matter events: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Starting Matter Commission Test... + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +===> Got a Matter Event: CHIPoBLE Advertising Change +===> Got a Matter Event: Commissioning Window Opened +===> Got a Matter Event: Commissioning Session Started +===> Got a Matter Event: Commissioning Complete +===> Got a Matter Event: Operational Network Started +===> Got a Matter Event: Operational Network Enabled +===> Got a Matter Event: Internet Connectivity Change :: IPv4 Connectivity: Established - IP Address: 192.168.1.100 +===> Got a Matter Event: IPv4 Address Assigned +===> Got a Matter Event: Server Ready +===> Got a Matter Event: DNS-SD Initialized +Matter Fabric not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to Wi-Fi. +====> Decommissioning in 60 seconds. <==== +===> Got a Matter Event: Fabric Will Be Removed +===> Got a Matter Event: Fabric Removed +===> Got a Matter Event: Commissioning Window Opened +Matter Node is decommissioned. Commissioning widget shall start over. +``` + +## Using the Device + +### Event Monitoring + +The example continuously monitors and displays Matter events to the Serial Monitor. This includes: + +- **Connectivity Events**: Wi-Fi, Thread, Internet connectivity changes, IP address assignments +- **Commissioning Events**: Commissioning session start/stop, commissioning window open/close, commissioning complete +- **Fabric Events**: Fabric committed, updated, removed +- **BLE Events**: CHIPoBLE connection established/closed, advertising changes +- **Network Events**: Operational network started/enabled, interface IP address changes +- **Service Events**: Service connectivity changes, provisioning changes, DNS-SD events +- **System Events**: Server ready, OTA state changes, time sync changes, fail-safe timer expiration + +### Test Cycle + +The device operates in a continuous test cycle: + +1. **Commissioning Phase**: The device waits for Matter commissioning and displays all related events. +2. **Commissioned Phase**: Once commissioned, the device is connected to the Matter network and ready for use. All network and service events are displayed. +3. **Automatic Decommissioning**: After 60 seconds, the device automatically decommissions itself. +4. **Repeat**: The cycle repeats, allowing you to test the commissioning process multiple times and observe all events. + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device during each test cycle. Monitor the Serial Monitor to see all events that occur during the commissioning process. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. Monitor the Serial Monitor to see all Matter events during commissioning +7. The device will appear as an on/off light in your Home app +8. After 60 seconds, the device will automatically decommission and the cycle will repeat + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. Monitor the Serial Monitor to see all Matter events during commissioning +6. The light will appear in your Alexa app +7. After 60 seconds, the device will automatically decommission and the cycle will repeat + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. Monitor the Serial Monitor to see all Matter events during commissioning +7. After 60 seconds, the device will automatically decommission and the cycle will repeat + +## Code Structure + +The MatterEvents example consists of the following main components: + +1. **`setup()`**: Initializes Serial communication, configures Wi-Fi (if needed), sets up the Matter On/Off Light endpoint, registers the Matter event callback handler, and starts the Matter stack. +2. **`loop()`**: Checks the Matter commissioning state, displays pairing information when not commissioned, waits for commissioning, and then automatically decommissions after 60 seconds to repeat the cycle. +3. **`onMatterEvent()`**: Comprehensive event callback handler that processes and displays all Matter events, including connectivity changes, commissioning events, fabric management, BLE events, and system events. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **No events displayed**: Verify that the event callback is properly registered using `Matter.onEvent()` +- **Failed to commission**: Try waiting for the next cycle after decommissioning. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **Device keeps decommissioning**: This is expected behavior - the device automatically decommissions after 60 seconds to allow continuous testing and event monitoring + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterEvents/ci.json b/libraries/Matter/examples/MatterEvents/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterEvents/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterEvents/ci.yml b/libraries/Matter/examples/MatterEvents/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterEvents/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterFan/README.md b/libraries/Matter/examples/MatterFan/README.md new file mode 100644 index 00000000000..cae9e15ba5f --- /dev/null +++ b/libraries/Matter/examples/MatterFan/README.md @@ -0,0 +1,200 @@ +# Matter Fan Example + +This example demonstrates how to create a Matter-compatible fan device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, manual control using a physical button, and analog input for speed control. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | PWM Pin | Status | +| --- | ---- | ------ | ----------------- | ------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a fan device +- Support for both Wi-Fi and Thread(*) connectivity +- On/Off control +- Speed control (0-100% in steps of 10%) +- Fan modes (OFF, ON, SMART, HIGH) +- Analog input for manual speed adjustment +- PWM output for DC motor control (simulated with RGB LED brightness) +- Button control for toggling fan and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- PWM-capable pin for DC motor control (uses RGB_BUILTIN or pin 2 by default) +- Analog input pin (A0) for speed control via potentiometer or similar +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **DC Motor PWM Pin**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 (for controlling fan speed) +- **Analog Input Pin**: A0 (for reading speed control input, 0-1023 mapped to 10-100% speed) +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **DC Motor PWM pin configuration** (if not using built-in RGB LED): + ```cpp + const uint8_t dcMotorPin = 2; // Set your PWM pin here + ``` + +3. **Analog input pin configuration** (optional): + By default, analog pin A0 is used for speed control. You can change this if needed. + ```cpp + const uint8_t analogPin = A0; // Set your analog pin here + ``` + +4. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Fan On/Off manual control and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterFan.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +Fan State: Mode OFF | 0% speed. +User button released. Setting the Fan ON. +Fan State: Mode ON | 50% speed. +Fan set to SMART mode -- speed percentage will go to 50% +Fan State: Mode SMART | 50% speed. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle fan on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Analog Speed Control + +The analog input pin (A0) allows manual speed adjustment: + +- Connect a potentiometer or similar analog input to pin A0 +- Analog values (0-1023) are mapped to speed percentages (10-100%) in steps of 10% +- The speed automatically updates when the analog input changes +- Speed changes are synchronized with the Matter controller + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a fan in your Home app +7. You can control on/off, speed (0-100%), and fan modes (OFF, ON, SMART, HIGH) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The fan will appear in your Alexa app +6. You can control speed and modes using voice commands like "Alexa, set fan to 50 percent" or "Alexa, set fan to high" + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control speed and modes using voice commands or the app controls + +## Code Structure + +The MatterFan example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, analog input, PWM output), configures Wi-Fi (if needed), sets up the Matter Fan endpoint with initial state (OFF, 0% speed), and registers callbacks for state changes. +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the fan and factory reset, reads analog input to adjust fan speed, and allows the Matter stack to process events. +3. **Callbacks**: + - `onChangeSpeedPercent()`: Handles speed percentage changes (0% to 100%). Automatically turns fan on/off based on speed. + - `onChangeMode()`: Handles fan mode changes (OFF, ON, SMART, HIGH). Automatically sets speed to 50% when switching from OFF to another mode. + - `onChange()`: Generic callback that controls the DC motor via PWM and reports the current state. + - `fanDCMotorDrive()`: Drives the DC motor (or simulates it with RGB LED brightness) based on fan state and speed. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Fan speed not responding**: Verify PWM pin configuration and connections. For DC motor control, ensure the pin supports PWM output +- **Analog input not working**: Verify analog pin configuration and that the input voltage is within the ESP32's ADC range (0-3.3V typically) +- **Speed changes not synchronized**: The analog input is read continuously, and speed updates only when the value changes by a step (0-9 steps mapped to 10-100%) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Fan Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_fan.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterFan/ci.json b/libraries/Matter/examples/MatterFan/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterFan/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterFan/ci.yml b/libraries/Matter/examples/MatterFan/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterFan/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterHumiditySensor/README.md b/libraries/Matter/examples/MatterHumiditySensor/README.md new file mode 100644 index 00000000000..838073093cf --- /dev/null +++ b/libraries/Matter/examples/MatterHumiditySensor/README.md @@ -0,0 +1,197 @@ +# Matter Humidity Sensor Example + +This example demonstrates how to create a Matter-compatible humidity sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sensor data reporting to smart home ecosystems, and automatic simulation of humidity readings. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a humidity sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Humidity measurement reporting (0-100%) +- Automatic simulation of humidity readings (10% to 30% range) +- Periodic sensor updates every 5 seconds +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect a real humidity sensor (DHT11, DHT22, SHT31, etc.) and replace the simulation function + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Real sensor integration** (optional): + To use a real humidity sensor, replace the `getSimulatedHumidity()` function with your sensor reading code. The function should return a float value representing humidity percentage (0-100%). + +## Building and Flashing + +1. Open the `MatterHumiditySensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +Current Humidity is 95.00% +Current Humidity is 10.50% +Current Humidity is 11.00% +Current Humidity is 11.50% +... +Current Humidity is 30.00% +Current Humidity is 10.00% +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Sensor Simulation + +The example includes a simulated humidity sensor that: + +- Starts at 95% humidity (initial value) +- Cycles through 10% to 30% humidity range +- Increases in 0.5% steps +- Updates every 5 seconds +- Resets to 10% when reaching 30% + +To use a real humidity sensor, replace the `getSimulatedHumidity()` function with your sensor library code. For example, with a DHT22: + +```cpp +#include +DHT dht(DHT_PIN, DHT22); + +float getSimulatedHumidity() { + return dht.readHumidity(); +} +``` + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a humidity sensor in your Home app +7. You can monitor the humidity readings and set up automations based on humidity levels + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The humidity sensor will appear in your Alexa app +6. You can monitor humidity readings and create routines based on humidity levels + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can monitor humidity readings and create automations based on humidity levels + +## Code Structure + +The MatterHumiditySensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Humidity Sensor endpoint with initial value (95%), and waits for Matter commissioning. +2. **`loop()`**: Displays the current humidity value every 5 seconds, updates the sensor reading from the simulated hardware sensor, handles button input for factory reset, and allows the Matter stack to process events. +3. **`getSimulatedHumidity()`**: Simulates a hardware humidity sensor by cycling through values from 10% to 30% in 0.5% steps. Replace this function with your actual sensor reading code. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Humidity readings not updating**: Check that the sensor simulation function is being called correctly. For real sensors, verify sensor wiring and library initialization + +- **Humidity values out of range**: Ensure humidity values are between 0-100%. The Matter protocol stores values as 1/100th of a percent internally + +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Humidity Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_humidity_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterHumiditySensor/ci.json b/libraries/Matter/examples/MatterHumiditySensor/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterHumiditySensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterHumiditySensor/ci.yml b/libraries/Matter/examples/MatterHumiditySensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterHumiditySensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/README.md b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/README.md new file mode 100644 index 00000000000..62c923e3655 --- /dev/null +++ b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/README.md @@ -0,0 +1,210 @@ +# Matter Lambda Single Callback Many Endpoints Example + +This example demonstrates how to create multiple Matter endpoints in a single node using a shared lambda function callback with capture in an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, multiple endpoint management, and efficient callback handling using C++ lambda functions with capture variables. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | GPIO Pins | Status | +| --- | ---- | ------ | ----------------- | --------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation with multiple endpoints in a single node +- Six on/off light endpoints sharing a single callback function +- Lambda function with capture variable for efficient endpoint identification +- Support for both Wi-Fi and Thread(*) connectivity +- Each endpoint has a unique GPIO pin and friendly name +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- Optional: Six LEDs connected to GPIO pins (2, 4, 6, 8, 10, 12) for visual feedback + +## Pin Configuration + +By default, the example uses six GPIO pins for the on/off lights: +- **Light 1 (Room 1)**: GPIO 2 +- **Light 2 (Room 2)**: GPIO 4 +- **Light 3 (Room 3)**: GPIO 6 +- **Light 4 (Room 4)**: GPIO 8 +- **Light 5 (Room 5)**: GPIO 10 +- **Light 6 (Room 6)**: GPIO 12 + +You can modify the `lightPins` array to match your hardware configuration: + +```cpp +uint8_t lightPins[MAX_LIGHT_NUMBER] = {2, 4, 6, 8, 10, 12}; +``` + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **GPIO pin configuration** (optional): + Modify the `lightPins` array to match your hardware: + ```cpp + uint8_t lightPins[MAX_LIGHT_NUMBER] = {2, 4, 6, 8, 10, 12}; + ``` + +3. **Light names** (optional): + Modify the `lightName` array to customize the friendly names: + ```cpp + const char *lightName[MAX_LIGHT_NUMBER] = { + "Room 1", "Room 2", "Room 3", "Room 4", "Room 5", "Room 6", + }; + ``` + +4. **Number of endpoints** (optional): + Change `MAX_LIGHT_NUMBER` to create more or fewer endpoints: + ```cpp + const uint8_t MAX_LIGHT_NUMBER = 6; + ``` + +## Building and Flashing + +1. Open the `MatterLambdaSingleCallbackManyEPs.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +Matter App Control: 'Room 1' (OnOffLight[0], Endpoint 1, GPIO 2) changed to: OFF +Matter App Control: 'Room 1' (OnOffLight[0], Endpoint 1, GPIO 2) changed to: ON +Matter App Control: 'Room 5' (OnOffLight[4], Endpoint 5, GPIO 10) changed to: ON +Matter App Control: 'Room 2' (OnOffLight[1], Endpoint 2, GPIO 4) changed to: ON +``` + +## Using the Device + +### Lambda Function with Capture + +This example demonstrates how to use C++ lambda functions with capture variables to efficiently handle multiple endpoints with a single callback function. The lambda capture `[i]` allows the callback to identify which endpoint triggered the event: + +```cpp +OnOffLight[i].onChangeOnOff([i](bool state) -> bool { + Serial.printf( + "Matter App Control: '%s' (OnOffLight[%d], Endpoint %d, GPIO %d) changed to: %s\r\n", + lightName[i], i, OnOffLight[i].getEndPointId(), lightPins[i], state ? "ON" : "OFF" + ); + return true; +}); +``` + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. After commissioning, you will see six separate on/off light devices in your smart home app, each representing a different room. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as six separate on/off lights in your Home app (Room 1 through Room 6) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The six lights will appear in your Alexa app +6. You can control each room independently + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The six lights will appear in your Google Home app + +## Code Structure + +The MatterLambdaSingleCallbackManyEPs example consists of the following main components: + +1. **Arrays and Configuration**: + - `OnOffLight[MAX_LIGHT_NUMBER]`: Array of Matter on/off light endpoints + - `lightPins[]`: Array of GPIO pins for each light + - `lightName[]`: Array of friendly names for each light + +2. **`setup()`**: Configures Wi-Fi (if needed), initializes all GPIO pins, initializes all Matter endpoints, registers lambda callbacks with capture variables for each endpoint, and starts the Matter stack. + +3. **`loop()`**: Checks the Matter commissioning state and connection status, displays appropriate messages, and allows the Matter stack to process events. + +4. **Lambda Callback**: + - Uses capture variable `[i]` to identify which endpoint triggered the callback + - Displays detailed information including friendly name, array index, endpoint ID, GPIO pin, and state + - Demonstrates efficient callback handling for multiple endpoints + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Only some endpoints appear**: Some smart home platforms may group or display endpoints differently. Check your app's device list +- **GPIO pins not responding**: Verify pin configurations match your hardware. Ensure pins are not used by other peripherals +- **Failed to commission**: Try erasing the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **Compilation errors with lambda functions**: Ensure you're using a C++11 compatible compiler (ESP32 Arduino Core 2.0+ supports this) + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.json b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.yml b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterMinimum/README.md b/libraries/Matter/examples/MatterMinimum/README.md new file mode 100644 index 00000000000..b3687718543 --- /dev/null +++ b/libraries/Matter/examples/MatterMinimum/README.md @@ -0,0 +1,176 @@ +# Matter Minimum Example + +This example demonstrates the smallest code required to create a Matter-compatible device using an ESP32 SoC microcontroller.\ +The application showcases the minimal implementation for Matter commissioning and device control via smart home ecosystems. This is an ideal starting point for understanding Matter basics and building more complex devices. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Optional | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Optional | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Optional | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Optional | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Optional | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Optional | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Optional | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Minimal Matter protocol implementation for an on/off light device +- Support for both Wi-Fi and Thread(*) connectivity +- Simple on/off control via Matter app +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +- Minimal code footprint - ideal for learning Matter basics +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- Optional: LED connected to GPIO pin (or using built-in LED) for visual feedback +- Optional: User button for factory reset (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `LED_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default (only for factory reset) + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterMinimum.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +``` + +After commissioning, the device will be ready for control via Matter apps. No additional status messages are printed in this minimal example. + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +Note: This minimal example does not include button toggle functionality. To add manual toggle control, you can extend the code with additional button handling logic. + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an on/off light in your Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup + +## Code Structure + +The MatterMinimum example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), initializes the Matter on/off light endpoint, registers the callback function, and starts the Matter stack. Displays commissioning information if not yet commissioned. + +2. **`loop()`**: Handles button input for factory reset (long press >5 seconds) and allows the Matter stack to process events. This minimal example does not include commissioning state checking in the loop - it only checks once in setup. + +3. **`onOffLightCallback()`**: Simple callback function that controls the LED based on the on/off state received from the Matter controller. This is the minimal callback implementation. + +## Extending the Example + +This minimal example can be extended with additional features: + +- **State persistence**: Add `Preferences` library to save the last known state +- **Button toggle**: Add button press detection to toggle the light manually +- **Commissioning status check**: Add periodic checking of commissioning state in the loop +- **Multiple endpoints**: Add more Matter endpoints to the same node +- **Enhanced callbacks**: Add more detailed callback functions for better control + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections. The LED will only respond to Matter app commands after commissioning +- **Failed to commission**: Try erasing the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **LED not turning on/off**: Ensure the device is commissioned and you're controlling it via a Matter app. The minimal example only responds to Matter controller commands, not local button presses + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterMinimum/ci.json b/libraries/Matter/examples/MatterMinimum/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterMinimum/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterMinimum/ci.yml b/libraries/Matter/examples/MatterMinimum/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterMinimum/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterOccupancySensor/README.md b/libraries/Matter/examples/MatterOccupancySensor/README.md new file mode 100644 index 00000000000..cb38c63ac6a --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancySensor/README.md @@ -0,0 +1,273 @@ +# Matter Occupancy Sensor Example + +This example demonstrates how to create a Matter-compatible occupancy sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sensor data reporting to smart home ecosystems, and automatic simulation of occupancy state changes. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an occupancy sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Occupancy state reporting (Occupied/Unoccupied) +- Automatic simulation of occupancy state changes every 2 minutes +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: PIR (Passive Infrared) motion sensor (e.g., HC-SR501, AM312) for real occupancy detection + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default +- **PIR Sensor** (optional): Connect to any available GPIO pin (e.g., GPIO 4) when using a real sensor + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **PIR sensor pin configuration** (optional, if using a real PIR sensor): + ```cpp + const uint8_t pirPin = 4; // Set your PIR sensor pin here + ``` + See the "PIR Sensor Integration Example" section below for complete integration instructions. + +## Building and Flashing + +1. Open the `MatterOccupancySensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +``` + +After commissioning, the occupancy sensor will automatically toggle between occupied and unoccupied states every 2 minutes, and the Matter controller will receive these state updates. + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Sensor Simulation + +The example includes a simulated occupancy sensor that: + +- Starts in the unoccupied state (false) +- Toggles between occupied (true) and unoccupied (false) every 2 minutes +- Updates the Matter attribute automatically + +To use a real occupancy sensor, replace the `simulatedHWOccupancySensor()` function with your sensor library code. + +### PIR Sensor Integration Example + +Here's a complete example for integrating a simple PIR (Passive Infrared) motion sensor: + +#### Hardware Connections + +Connect the PIR sensor to your ESP32: +- **PIR VCC** → ESP32 3.3 V or 5 V (check your PIR sensor specifications) +- **PIR GND** → ESP32 GND +- **PIR OUT** → ESP32 GPIO pin (e.g., GPIO 4) + +Common PIR sensors (HC-SR501, AM312, etc.) typically have three pins: VCC, GND, and OUT (digital output). + +#### Code Modifications + +1. **Add PIR pin definition** at the top of the sketch (after the button pin definition): + +```cpp +// PIR sensor pin +const uint8_t pirPin = 4; // Change this to your PIR sensor pin +``` + +2. **Initialize PIR pin in setup()** (after button initialization): + +```cpp +void setup() { + // ... existing code ... + pinMode(buttonPin, INPUT_PULLUP); + + // Initialize PIR sensor pin + pinMode(pirPin, INPUT); + + // ... rest of setup code ... +} +``` + +3. **Replace the simulated function** with the real PIR reading function: + +```cpp +bool simulatedHWOccupancySensor() { + // Read PIR sensor digital input + // HIGH = motion detected (occupied), LOW = no motion (unoccupied) + return digitalRead(pirPin) == HIGH; +} +``` + +#### Complete Modified Function Example + +Here's the complete modified function with debouncing for more reliable readings: + +```cpp +bool simulatedHWOccupancySensor() { + // Read PIR sensor with debouncing + static bool lastState = false; + static uint32_t lastChangeTime = 0; + const uint32_t debounceTime = 100; // 100ms debounce + + bool currentState = digitalRead(pirPin) == HIGH; + + // Only update if state has changed and debounce time has passed + if (currentState != lastState) { + if (millis() - lastChangeTime > debounceTime) { + lastState = currentState; + lastChangeTime = millis(); + Serial.printf("Occupancy state changed: %s\r\n", currentState ? "OCCUPIED" : "UNOCCUPIED"); + } + } + + return lastState; +} +``` + +#### Testing the PIR Sensor + +After making these changes: +1. Upload the modified sketch to your ESP32 +2. Open the Serial Monitor at 115200 baud +3. Move in front of the PIR sensor - you should see "OCCUPIED" messages +4. Stay still for a few seconds - you should see "UNOCCUPIED" messages +5. The Matter controller will automatically receive these occupancy state updates + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an occupancy sensor in your Home app +7. You can monitor the occupancy state and set up automations based on occupancy (e.g., turn on lights when occupied) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The occupancy sensor will appear in your Alexa app +6. You can monitor occupancy readings and create routines based on occupancy state changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can monitor occupancy readings and create automations based on occupancy state changes + +## Code Structure + +The MatterOccupancySensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Occupancy Sensor endpoint with initial state (unoccupied), and waits for Matter commissioning. + +2. **`loop()`**: Handles button input for factory reset, continuously checks the simulated occupancy sensor and updates the Matter attribute, and allows the Matter stack to process events. + +3. **`simulatedHWOccupancySensor()`**: Simulates a hardware occupancy sensor by toggling the occupancy state every 2 minutes. Replace this function with your actual sensor reading code. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Occupancy readings not updating**: Check that the sensor simulation function is being called correctly. For real sensors, verify sensor wiring and library initialization +- **State not changing**: The simulated sensor toggles every 2 minutes (120000 ms). If you're using a real sensor, ensure it's properly connected and reading correctly +- **PIR sensor not detecting motion**: + - Verify PIR sensor wiring (VCC, GND, OUT connections) + - Check if PIR sensor requires 5 V or 3.3 V power (some PIR sensors need 5 V) + - Allow 30-60 seconds for PIR sensor to stabilize after power-on + - Adjust PIR sensor sensitivity and time delay potentiometers (if available on your sensor) + - Ensure the PIR sensor has a clear view of the detection area + - Test the PIR sensor directly by reading the GPIO pin value in Serial Monitor +- **PIR sensor false triggers**: Add debouncing to the sensor reading function (see the "Complete Modified Function Example" above) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Occupancy Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_occupancy_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterOccupancySensor/ci.json b/libraries/Matter/examples/MatterOccupancySensor/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterOccupancySensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterOccupancySensor/ci.yml b/libraries/Matter/examples/MatterOccupancySensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancySensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterOnIdentify/README.md b/libraries/Matter/examples/MatterOnIdentify/README.md new file mode 100644 index 00000000000..ce873f9397f --- /dev/null +++ b/libraries/Matter/examples/MatterOnIdentify/README.md @@ -0,0 +1,219 @@ +# Matter On Identify Example + +This example demonstrates how to implement the Matter Identify cluster callback for an on/off light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and the Identify feature that makes the LED blink when the device is identified from a Matter app. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an on/off light device +- Support for both Wi-Fi and Thread(*) connectivity +- On Identify callback implementation - LED blinks when device is identified +- Visual identification feedback (red blinking for RGB LED, toggling for regular LED) +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pin (or using built-in LED) for visual feedback +- Optional: RGB LED for red blinking identification (uses RGB_BUILTIN if available) +- User button for factory reset (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `LED_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterOnIdentify.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +``` + +When you trigger the Identify command from a Matter app, you should see: +``` +Identify Cluster is Active +Identify Cluster is Inactive +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Identify Feature + +The Identify feature allows you to visually identify a specific device from your Matter app. When you trigger the Identify command: + +1. **For RGB LED (RGB_BUILTIN)**: The LED will blink in red color +2. **For regular LED**: The LED will toggle on/off + +The blinking continues while the Identify cluster is active (typically 3-15 seconds depending on the app). When the Identify period ends, the LED automatically returns to its previous state (on or off). + +### How to Trigger Identify + +#### Apple Home + +1. Open the Home app on your iOS device +2. Find your device in the device list +3. Long press on the device +4. Tap "Identify" or look for the identify option in device settings +5. The LED will start blinking + +#### Amazon Alexa + +1. Open the Alexa app +2. Navigate to your device +3. Look for "Identify" or "Find Device" option in device settings +4. The LED will start blinking + +#### Google Home + +1. Open the Google Home app +2. Select your device +3. Look for "Identify" or "Find Device" option +4. The LED will start blinking + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an on/off light in your Home app +7. Use the Identify feature to visually locate the device + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app +6. Use the Identify feature to visually locate the device + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. Use the Identify feature to visually locate the device + +## Code Structure + +The MatterOnIdentify example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), initializes the Matter on/off light endpoint, registers the on/off callback and the Identify callback, and starts the Matter stack. + +2. **`loop()`**: Handles the Identify blinking logic (if identify flag is active, blinks the LED every 500 ms), handles button input for factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + - `onOffLightCallback()`: Controls the physical LED based on on/off state from Matter controller. + - `onIdentifyLightCallback()`: Handles the Identify cluster activation/deactivation. When active, sets the identify flag to start blinking. When inactive, stops blinking and restores the original light state. + +4. **Identify Blinking Logic**: + - For RGB LEDs: Blinks in red color (brightness 32) when identify is active + - For regular LEDs: Toggles on/off when identify is active + - Blinking rate: Every 500 ms (determined by the delay in loop) + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **Identify feature not working**: Ensure the device is commissioned and you're using a Matter app that supports the Identify cluster. Some apps may not have a visible Identify button +- **LED not blinking during identify**: Check Serial Monitor for "Identify Cluster is Active" message. If you don't see it, the Identify command may not be reaching the device +- **LED state not restored after identify**: The code uses a double-toggle to restore state. If this doesn't work, ensure the light state is properly tracked +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter On/Off Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_on_off_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterOnIdentify/ci.json b/libraries/Matter/examples/MatterOnIdentify/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterOnIdentify/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterOnIdentify/ci.yml b/libraries/Matter/examples/MatterOnIdentify/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterOnIdentify/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterOnOffLight/README.md b/libraries/Matter/examples/MatterOnOffLight/README.md new file mode 100644 index 00000000000..b973302129a --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffLight/README.md @@ -0,0 +1,187 @@ +# Matter On/Off Light Example + +This example demonstrates how to create a Matter-compatible on/off light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and manual control using a physical button with state persistence. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an on/off light device +- Support for both Wi-Fi and Thread(*) connectivity +- Simple on/off control +- State persistence using `Preferences` library +- Button control for toggling light and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pin (or using built-in LED) for visual feedback +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `LED_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Light On/Off manual control and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterOnOffLight.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: ON +Matter Node is commissioned and connected to the network. Ready for use. +User Callback :: New Light State = ON +User Callback :: New Light State = OFF +User button released. Toggling Light! +User Callback :: New Light State = ON +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle light on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### State Persistence + +The device saves the last known on/off state using the `Preferences` library. After a power cycle or restart: + +- The device will restore to the last saved state (ON or OFF) +- The Matter controller will be notified of the restored state +- The LED will reflect the restored state + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an on/off light in your Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app +6. You can control it using voice commands like "Alexa, turn on the light" or "Alexa, turn off the light" + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control it using voice commands or the app controls + +## Code Structure + +The MatterOnOffLight example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), initializes `Preferences` library, sets up the Matter endpoint with the last saved state (defaults to ON if not previously saved), registers the callback function, and starts the Matter stack. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the light and factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + - `setLightOnOff()`: Controls the physical LED based on the on/off state, saves the state to `Preferences` for persistence, and prints the state change to Serial Monitor. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **State not persisting**: Check that the `Preferences` library is properly initialized and that flash memory is not corrupted +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **Button not toggling light**: Ensure the button is properly connected and the debounce time is appropriate. Check Serial Monitor for "User button released" messages +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter On/Off Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_on_off_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterOnOffLight/ci.json b/libraries/Matter/examples/MatterOnOffLight/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterOnOffLight/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterOnOffLight/ci.yml b/libraries/Matter/examples/MatterOnOffLight/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffLight/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterOnOffPlugin/README.md b/libraries/Matter/examples/MatterOnOffPlugin/README.md new file mode 100644 index 00000000000..2603961353c --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffPlugin/README.md @@ -0,0 +1,204 @@ +# Matter On/Off Plugin Example + +This example demonstrates how to create a Matter-compatible on/off plugin unit (power relay) device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and state persistence for power control applications. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Relay/LED | Status | +| --- | ---- | ------ | ----------------- | --------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for an on/off plugin unit (power relay) device +- Support for both Wi-Fi and Thread(*) connectivity +- Simple on/off control for power management +- State persistence using `Preferences` library +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- Power relay module or LED for visualization (for testing, uses built-in LED) +- User button for factory reset (uses BOOT button by default) + +## Pin Configuration + +- **Power Relay/Plugin Pin**: Uses `LED_BUILTIN` if defined (for testing), otherwise pin 2. For production use, connect this to your relay control pin. +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Power relay pin configuration** (if not using built-in LED): + For production use, change this to the GPIO pin connected to your relay control module: + ```cpp + const uint8_t onoffPin = 2; // Set your relay control pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterOnOffPlugin.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: OFF +Matter Node is commissioned and connected to the network. Ready for use. +User Callback :: New Plugin State = ON +User Callback :: New Plugin State = OFF +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +Note: This example does not include button toggle functionality. The plugin is controlled exclusively via Matter app commands. + +### State Persistence + +The device saves the last known on/off state using the `Preferences` library. After a power cycle or restart: + +- The device will restore to the last saved state (ON or OFF) +- Default state is OFF if no previous state was saved +- The Matter controller will be notified of the restored state +- The relay/LED will reflect the restored state + +### Power Relay Integration + +For production use with a power relay module: + +1. **Connect the relay module** to your ESP32: + - Relay VCC → ESP32 3.3 V or 5 V (check relay module specifications) + - Relay GND → ESP32 GND + - Relay IN → ESP32 GPIO pin (configured as `onoffPin`) + +2. **Update the pin configuration** in the sketch: + ```cpp + const uint8_t onoffPin = 2; // Your relay control pin + ``` + +3. **Test the relay** by controlling it via Matter app - the relay should turn on/off accordingly + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as an on/off switch/outlet in your Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The plugin will appear in your Alexa app +6. You can control it using voice commands like "Alexa, turn on the plugin" or "Alexa, turn off the plugin" + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control it using voice commands or the app controls + +## Code Structure + +The MatterOnOffPlugin example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, relay/LED pin), configures Wi-Fi (if needed), initializes `Preferences` library, sets up the Matter plugin endpoint with the last saved state (defaults to OFF if not previously saved), registers the callback function, and starts the Matter stack. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + - `setPluginOnOff()`: Controls the physical relay/LED based on the on/off state, saves the state to `Preferences` for persistence, and prints the state change to Serial Monitor. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Relay/LED not responding**: Verify pin configurations and connections. For relay modules, ensure proper power supply and wiring +- **State not persisting**: Check that the `Preferences` library is properly initialized and that flash memory is not corrupted +- **Relay not switching**: For relay modules, verify the control signal voltage levels match your relay module requirements (some relays need 5 V, others work with 3.3 V) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter On/Off Plugin Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_on_off_plugin.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterOnOffPlugin/ci.json b/libraries/Matter/examples/MatterOnOffPlugin/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterOnOffPlugin/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterOnOffPlugin/ci.yml b/libraries/Matter/examples/MatterOnOffPlugin/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffPlugin/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterPressureSensor/README.md b/libraries/Matter/examples/MatterPressureSensor/README.md new file mode 100644 index 00000000000..2f0665067ac --- /dev/null +++ b/libraries/Matter/examples/MatterPressureSensor/README.md @@ -0,0 +1,197 @@ +# Matter Pressure Sensor Example + +This example demonstrates how to create a Matter-compatible pressure sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sensor data reporting to smart home ecosystems, and automatic simulation of pressure readings. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a pressure sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Pressure measurement reporting in hectopascals (hPa) +- Automatic simulation of pressure readings (950-1100 hPa range) +- Periodic sensor updates every 5 seconds +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect a real pressure sensor (BMP280, BME280, BMP388, etc.) and replace the simulation function + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Real sensor integration** (optional): + To use a real pressure sensor, replace the `getSimulatedPressure()` function with your sensor reading code. The function should return a float value representing pressure in hectopascals (hPa). + +## Building and Flashing + +1. Open the `MatterPressureSensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +Current Pressure is 900.00hPa +Current Pressure is 950.00hPa +Current Pressure is 960.00hPa +... +Current Pressure is 1100.00hPa +Current Pressure is 950.00hPa +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Sensor Simulation + +The example includes a simulated pressure sensor that: + +- Starts at 900 hPa (initial value) +- Cycles through 950 to 1100 hPa range +- Increases in 10 hPa steps +- Updates every 5 seconds +- Resets to 950 hPa when reaching 1100 hPa + +To use a real pressure sensor, replace the `getSimulatedPressure()` function with your sensor library code. For example, with a BMP280: + +```cpp +#include +Adafruit_BMP280 bmp; + +float getSimulatedPressure() { + return bmp.readPressure() / 100.0; // Convert Pa to hPa +} +``` + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a pressure sensor in your Home app +7. You can monitor the pressure readings and set up automations based on pressure levels (e.g., weather monitoring) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The pressure sensor will appear in your Alexa app +6. You can monitor pressure readings and create routines based on pressure changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can monitor pressure readings and create automations based on pressure changes + +## Code Structure + +The MatterPressureSensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Pressure Sensor endpoint with initial value (900 hPa), and waits for Matter commissioning. + +2. **`loop()`**: Displays the current pressure value every 5 seconds, updates the sensor reading from the simulated hardware sensor, handles button input for factory reset, and allows the Matter stack to process events. + +3. **`getSimulatedPressure()`**: Simulates a hardware pressure sensor by cycling through values from 950 to 1100 hPa in 10 hPa steps. Replace this function with your actual sensor reading code. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Pressure readings not updating**: Check that the sensor simulation function is being called correctly. For real sensors, verify sensor wiring and library initialization +- **Pressure values out of range**: Ensure pressure values are in hectopascals (hPa). The Matter protocol stores values as uint16_t internally. Typical atmospheric pressure ranges from 950-1050 hPa at sea level +- **State not changing**: The simulated sensor increases by 10 hPa every 5 seconds. If you're using a real sensor, ensure it's properly connected and reading correctly +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Pressure Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_pressure_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterPressureSensor/ci.json b/libraries/Matter/examples/MatterPressureSensor/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterPressureSensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterPressureSensor/ci.yml b/libraries/Matter/examples/MatterPressureSensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterPressureSensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterRainSensor/MatterRainSensor.ino b/libraries/Matter/examples/MatterRainSensor/MatterRainSensor.ino new file mode 100644 index 00000000000..93242fbf4f5 --- /dev/null +++ b/libraries/Matter/examples/MatterRainSensor/MatterRainSensor.ino @@ -0,0 +1,161 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * This example is an example code that will create a Matter Device which can be + * commissioned and controlled from a Matter Environment APP. + * Additionally the ESP32 will send debug messages indicating the Matter activity. + * Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. + * + * The example will create a Matter Rain Sensor Device. + * The Rain Sensor state can be toggled by pressing the onboard button. + * The Rain Sensor state will be indicated by the onboard LED. + * The Rain Sensor state will be simulated to change every 20 seconds. + * + * The onboard button can be kept pressed for 5 seconds to decommission the Matter Node. + * The example will also show the manual commissioning code and QR code to be used in the Matter environment. + * + */ + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif + +// List of Matter Endpoints for this Node +// Matter Rain Sensor Endpoint +MatterRainSensor RainSensor; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// LED will be used to indicate the Rain Sensor state +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t ledPin = RGB_BUILTIN; +#else +const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here - decommissioning and Manual Rain Sensor toggle button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debouceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + // The button will also be used to manually toggle the Rain Sensor state + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the LED (light) GPIO and Matter End Point + pinMode(ledPin, OUTPUT); + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); +#endif + + // set initial rain sensor state as false (default) + RainSensor.begin(); + digitalWrite(ledPin, LOW); // LED OFF + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Rain Sensor Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } +} + +bool simulatedHWRainSensor() { + // Simulated Rain Sensor + static bool rainState = false; + static uint32_t lastTime = 0; + + // Simulate a Rain Sensor state change every 20 seconds + if (millis() - lastTime > 20000) { + rainState = !rainState; + lastTime = millis(); + } + return rainState; +} + +void loop() { + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) { + button_state = false; // released + // button is released - toggle Rain State (Not Detected/Detected) + RainSensor.setRain(!RainSensor.getRain()); // same as RainSensor = !RainSensor; + Serial.printf("User button released. Setting the Rain Sensor to %s.\r\n", RainSensor ? "Detected" : "Not Detected"); + // LED will indicate the Rain Sensor state + if (RainSensor) { + digitalWrite(ledPin, HIGH); // LED ON + } else { + digitalWrite(ledPin, LOW); // LED OFF + } + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning Rain Sensor Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so + } + + // Simulated Rain Sensor + RainSensor.setRain(simulatedHWRainSensor()); + + delay(50); +} diff --git a/libraries/Matter/examples/MatterRainSensor/README.md b/libraries/Matter/examples/MatterRainSensor/README.md new file mode 100644 index 00000000000..1e81b3ea3c9 --- /dev/null +++ b/libraries/Matter/examples/MatterRainSensor/README.md @@ -0,0 +1,181 @@ +# Matter Rain Sensor Example + +This example demonstrates how to create a Matter-compatible rain sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, manual control using a physical button, and automatic simulation of rain detection state changes. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a rain sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Rain detection state indication using LED (ON = Detected, OFF = Not Detected) +- Automatic simulation of rain detection state changes every 20 seconds +- Button control for toggling rain detection state and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pins (or using built-in LED) to indicate rain detection state +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Rain Sensor state toggle and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterRainSensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +User button released. Setting the Rain Sensor to Detected. +User button released. Setting the Rain Sensor to Not Detected. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle rain sensor state (Not Detected/Detected) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Automatic Simulation + +The rain sensor state automatically toggles every 20 seconds to simulate a real rain sensor. The LED will reflect the current state: +- **LED ON**: Rain is Detected +- **LED OFF**: Rain is Not Detected + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. +Check for Matter Rain Sensor endpoint support within the Matter Controller developer webpage. +This endpoint is part of the latest Matter supported device list and it may not be fully supported by your Matter environment. +You can also try the Home Assistant Matter feature in order to test it. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a rain sensor in your Home app +7. You can monitor the rain detection state (Detected/Not Detected) and receive notifications when the state changes + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The rain sensor will appear in your Alexa app +6. You can monitor the rain detection state and set up routines based on state changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The rain sensor will appear in your Google Home app + +## Code Structure + +The MatterRainSensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter Rain Sensor endpoint with initial state (Not Detected), and waits for Matter commissioning. +2. **`loop()`**: Handles button input for toggling rain detection state and factory reset, and automatically simulates rain detection state changes every 20 seconds. +3. **`simulatedHWRainSensor()`**: Simulates a hardware rain sensor by toggling the rain detection state every 20 seconds. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **Rain sensor state not updating**: Check Serial Monitor output to verify state changes are being processed +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Rain Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_rain_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterRainSensor/ci.yml b/libraries/Matter/examples/MatterRainSensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterRainSensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterSmartButon/ci.json b/libraries/Matter/examples/MatterSmartButon/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterSmartButon/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterSmartButon/MatterSmartButon.ino b/libraries/Matter/examples/MatterSmartButton/MatterSmartButton.ino similarity index 100% rename from libraries/Matter/examples/MatterSmartButon/MatterSmartButon.ino rename to libraries/Matter/examples/MatterSmartButton/MatterSmartButton.ino diff --git a/libraries/Matter/examples/MatterSmartButton/README.md b/libraries/Matter/examples/MatterSmartButton/README.md new file mode 100644 index 00000000000..e7290ba40b3 --- /dev/null +++ b/libraries/Matter/examples/MatterSmartButton/README.md @@ -0,0 +1,181 @@ +# Matter Smart Button Example + +This example demonstrates how to create a Matter-compatible smart button (generic switch) device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sending button click events to smart home ecosystems, and triggering automations based on button presses. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a smart button (generic switch) device +- Support for both Wi-Fi and Thread(*) connectivity +- Button click event reporting to Matter controller +- Button control for triggering events and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +- Automation trigger support - button presses can trigger actions in smart home apps +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for triggering events (uses BOOT button by default) + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for triggering click events and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterSmartButton.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +User button released. Sending Click to the Matter Controller! +User button released. Sending Click to the Matter Controller! +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides the following functionality: + +- **Short press and release**: Sends a click event to the Matter controller (triggers automations) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Button Click Events + +When you press and release the button: + +1. The button press is detected and debounced +2. A click event is sent to the Matter controller via the Generic Switch cluster +3. The Matter controller receives the event and can trigger programmed automations +4. The event is logged to Serial Monitor for debugging + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. After commissioning, you can set up automations that trigger when the button is pressed. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a switch/button in your Home app +7. Set up automations: Go to Automation tab > Create Automation > When an accessory is controlled > Select the smart button > Choose the action (e.g., turn on lights, activate scene) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The smart button will appear in your Alexa app +6. Create routines: Tap More > Routines > Create Routine > When this happens > Select the smart button > Add action (e.g., turn on lights, control other devices) + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. Create automations: Tap Automations > Create Automation > When button is pressed > Choose action + +## Code Structure + +The MatterSmartButton example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), initializes the Matter Generic Switch endpoint, and starts the Matter stack. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for sending click events and factory reset, and allows the Matter stack to process events. + +3. **Button Event Handling**: + - Detects button press and release with debouncing (250 ms) + - Sends click event to Matter controller using `SmartButton.click()` when button is released + - Handles long press (>5 seconds) for factory reset + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Button clicks not registering**: Check Serial Monitor for "User button released" messages. Verify button wiring and that debounce time is appropriate +- **Automations not triggering**: Ensure the device is commissioned and that automations are properly configured in your Matter app. The button sends events, but automations must be set up in the app +- **Button not responding**: Verify button pin configuration and connections. Check that the button is properly connected with pull-up resistor (INPUT_PULLUP mode) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **Multiple clicks registered for single press**: Increase the debounce time in the code if needed + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Generic Switch Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_generic_switch.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterSmartButton/ci.yml b/libraries/Matter/examples/MatterSmartButton/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterSmartButton/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinet/MatterTemperatureControlledCabinet.ino b/libraries/Matter/examples/MatterTemperatureControlledCabinet/MatterTemperatureControlledCabinet.ino new file mode 100644 index 00000000000..ba02e44e68d --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinet/MatterTemperatureControlledCabinet.ino @@ -0,0 +1,246 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * This example demonstrates the Temperature Number mode of the Matter Temperature Controlled Cabinet Device. + * + * This example will create a Matter Device which can be commissioned and controlled from a Matter Environment APP. + * Additionally the ESP32 will send debug messages indicating the Matter activity. + * Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. + * + * The example will create a Matter Temperature Controlled Cabinet Device using temperature_number feature. + * The Temperature Controlled Cabinet can be controlled via Matter controllers to set + * temperature setpoint with min/max limits and optional step control. + * + * This mode is mutually exclusive with temperature_level mode. + * See MatterTemperatureControlledCabinetLevels example for temperature level control. + */ + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif + +// List of Matter Endpoints for this Node +// Matter Temperature Controlled Cabinet Endpoint +MatterTemperatureControlledCabinet TemperatureCabinet; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// set your board USER BUTTON pin here - decommissioning button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control - decommission the Matter Node +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Temperature control state +struct TemperatureControlState { + bool initialized; + bool increasing; + double currentSetpoint; + double initialSetpoint; + bool setpointReachedIncreasing; + bool setpointReachedDecreasing; +}; + +static TemperatureControlState tempState = { + .initialized = false, + .increasing = true, + .currentSetpoint = 0.0, + .initialSetpoint = 0.0, + .setpointReachedIncreasing = false, + .setpointReachedDecreasing = false +}; + +// Initialize temperature control state +void initTemperatureControl() { + if (!tempState.initialized) { + tempState.currentSetpoint = TemperatureCabinet.getTemperatureSetpoint(); + tempState.initialSetpoint = tempState.currentSetpoint; + tempState.initialized = true; + } +} + +// Check and log when initial setpoint is reached/overpassed +void checkSetpointReached(double newSetpoint, bool isIncreasing, bool directionChanged) { + if (directionChanged) { + // Reset flags when direction changes + tempState.setpointReachedIncreasing = false; + tempState.setpointReachedDecreasing = false; + return; + } + + if (isIncreasing && !tempState.setpointReachedIncreasing && newSetpoint >= tempState.initialSetpoint) { + Serial.printf("*** Temperature setpoint %.02f°C reached/overpassed while increasing ***\r\n", tempState.initialSetpoint); + tempState.setpointReachedIncreasing = true; + } else if (!isIncreasing && !tempState.setpointReachedDecreasing && newSetpoint <= tempState.initialSetpoint) { + Serial.printf("*** Temperature setpoint %.02f°C reached/overpassed while decreasing ***\r\n", tempState.initialSetpoint); + tempState.setpointReachedDecreasing = true; + } +} + +// Update temperature setpoint with cycling logic +void updateTemperatureSetpoint() { + double minTemp = TemperatureCabinet.getMinTemperature(); + double maxTemp = TemperatureCabinet.getMaxTemperature(); + double step = TemperatureCabinet.getStep(); + + // Calculate next setpoint based on direction and step + bool directionChanged = false; + + if (tempState.increasing) { + tempState.currentSetpoint += step; + if (tempState.currentSetpoint >= maxTemp) { + tempState.currentSetpoint = maxTemp; + tempState.increasing = false; // Reverse direction + directionChanged = true; + } + } else { + tempState.currentSetpoint -= step; + if (tempState.currentSetpoint <= minTemp) { + tempState.currentSetpoint = minTemp; + tempState.increasing = true; // Reverse direction + directionChanged = true; + } + } + + // Check if setpoint has been reached or overpassed + checkSetpointReached(tempState.currentSetpoint, tempState.increasing, directionChanged); + + // Update the temperature setpoint + if (TemperatureCabinet.setTemperatureSetpoint(tempState.currentSetpoint)) { + Serial.printf("Temperature setpoint updated to: %.02f°C (Range: %.02f°C to %.02f°C)\r\n", tempState.currentSetpoint, minTemp, maxTemp); + } else { + Serial.printf("Failed to update temperature setpoint to: %.02f°C\r\n", tempState.currentSetpoint); + } +} + +// Print current temperature status +void printTemperatureStatus() { + Serial.printf( + "Current Temperature Setpoint: %.02f°C (Range: %.02f°C to %.02f°C)\r\n", TemperatureCabinet.getTemperatureSetpoint(), + TemperatureCabinet.getMinTemperature(), TemperatureCabinet.getMaxTemperature() + ); +} + +// Handle button press for decommissioning +void handleButtonPress() { + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + if (digitalRead(buttonPin) == HIGH && button_state) { + button_state = false; // released + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning Temperature Controlled Cabinet Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so + } +} + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + pinMode(buttonPin, INPUT_PULLUP); + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); +#endif + + // Initialize Temperature Controlled Cabinet with: + // - Initial setpoint: 4.0°C (typical refrigerator temperature) + // - Min temperature: -10.0°C + // - Max temperature: 10.0°C + // - Step: 0.5°C (optional, for temperature_step feature) + TemperatureCabinet.begin(4.0, -10.0, 10.0, 0.5); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Temperature Controlled Cabinet Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } + + // Print initial configuration + Serial.println("\nTemperature Controlled Cabinet Configuration:"); + Serial.printf(" Setpoint: %.02f°C\n", TemperatureCabinet.getTemperatureSetpoint()); + Serial.printf(" Min Temperature: %.02f°C\n", TemperatureCabinet.getMinTemperature()); + Serial.printf(" Max Temperature: %.02f°C\n", TemperatureCabinet.getMaxTemperature()); + Serial.printf(" Step: %.02f°C\n", TemperatureCabinet.getStep()); +} + +void loop() { + static uint32_t timeCounter = 0; + static uint32_t lastUpdateTime = 0; + + // Initialize temperature control state on first run + initTemperatureControl(); + + // Update temperature setpoint dynamically every 1 second + uint32_t currentTime = millis(); + if (currentTime - lastUpdateTime >= 1000) { // 1 second interval + lastUpdateTime = currentTime; + updateTemperatureSetpoint(); + } + + // Print the current temperature setpoint every 5s + if (!(timeCounter++ % 10)) { // delaying for 500ms x 10 = 5s + printTemperatureStatus(); + } + + // Handle button press for decommissioning + handleButtonPress(); + + delay(500); +} diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinet/README.md b/libraries/Matter/examples/MatterTemperatureControlledCabinet/README.md new file mode 100644 index 00000000000..8ed6c69454a --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinet/README.md @@ -0,0 +1,215 @@ +# Matter Temperature Controlled Cabinet Example + +This example demonstrates how to create a Matter-compatible temperature controlled cabinet device using an ESP32 SoC microcontroller with the **temperature_number** feature mode. This mode provides precise temperature setpoint control with min/max limits and optional step control. + +**Important:** The `temperature_number` and `temperature_level` features are **mutually exclusive**. Only one can be enabled at a time. See `MatterTemperatureControlledCabinetLevels` example for temperature level control mode. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a temperature controlled cabinet device +- Support for both Wi-Fi and Thread(*) connectivity +- Temperature setpoint control with min/max limits +- Temperature step control (always enabled, can be set via begin() or setStep()) +- Temperature setpoint validation against min/max limits +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Use Case + +Use this mode when you need precise temperature control with specific setpoint values (e.g., 4.0°C for a refrigerator, -18.0°C for a freezer). For preset-based temperature control using levels, see the `MatterTemperatureControlledCabinetLevels` example. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect temperature control hardware (relays, heaters, coolers, etc.) to implement actual temperature control + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Temperature range configuration** (optional): + Adjust the initial temperature setpoint, min, max, and step values in the `begin()` call: + ```cpp + TemperatureCabinet.begin(4.0, -10.0, 10.0, 0.5); + // Parameters: setpoint, min_temp, max_temp, step (all in Celsius) + // Note: Step can also be set later using setStep() even if not provided here + ``` + +## Building and Flashing + +1. Open the `MatterTemperatureControlledCabinet.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. + +Temperature Controlled Cabinet Configuration: + Setpoint: 4.00°C + Min Temperature: -10.00°C + Max Temperature: 10.00°C + Step: 0.50°C +Temperature setpoint updated to: 4.50°C (Range: -10.00°C to 10.00°C) +*** Temperature setpoint 4.00°C reached/overpassed while increasing *** +Temperature setpoint updated to: 5.00°C (Range: -10.00°C to 10.00°C) +Temperature setpoint updated to: 5.50°C (Range: -10.00°C to 10.00°C) +... +Current Temperature Setpoint: 6.00°C (Range: -10.00°C to 10.00°C) +... +*** Temperature setpoint 4.00°C reached/overpassed while decreasing *** +Temperature setpoint updated to: 3.50°C (Range: -10.00°C to 10.00°C) +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a temperature controlled cabinet in your Home app +7. You can adjust the temperature setpoint within the min/max range + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The temperature controlled cabinet will appear in your Alexa app +6. You can control the temperature setpoint and set up routines + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The temperature controlled cabinet will appear in your Google Home app +7. You can control the temperature setpoint + +## Code Structure + +The MatterTemperatureControlledCabinet example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Temperature Controlled Cabinet endpoint with initial temperature configuration, and waits for Matter commissioning. + +2. **`loop()`**: + - **Dynamic Temperature Updates**: Automatically changes the temperature setpoint every 1 second, cycling between the minimum and maximum temperature limits using the configured step value. This demonstrates the temperature control functionality and allows Matter controllers to observe real-time changes. + - **Setpoint Reached Detection**: Monitors when the initial setpoint is reached or overpassed in each direction and prints a notification message once per direction. + - Periodically prints the current temperature setpoint (every 5 seconds) + - Handles button input for factory reset + +3. **Helper Functions**: + - `initTemperatureControl()`: Initializes the temperature control state from the current setpoint + - `checkSetpointReached()`: Checks and logs when the initial setpoint is reached/overpassed + - `updateTemperatureSetpoint()`: Updates the temperature setpoint with cycling logic and boundary detection + - `printTemperatureStatus()`: Prints the current temperature status + - `handleButtonPress()`: Handles button press detection and factory reset functionality + +## API Usage + +The example demonstrates the following API methods: + +- `begin(tempSetpoint, minTemperature, maxTemperature, step)` - Initialize the cabinet with temperature settings +- `getTemperatureSetpoint()` - Get current temperature setpoint +- `setTemperatureSetpoint(temperature)` - Set temperature setpoint (validated against min/max) +- `getMinTemperature()` / `getMaxTemperature()` - Get temperature limits +- `setMinTemperature(temperature)` / `setMaxTemperature(temperature)` - Set temperature limits +- `getStep()` / `setStep(step)` - Get/set temperature step value + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Temperature setpoint not updating**: Check Serial Monitor output to verify setpoint changes are being processed +- **Setpoint out of range error**: Ensure the setpoint is within the min/max temperature range +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Temperature Controlled Cabinet Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_temperature_controlled_cabinet.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinet/ci.yml b/libraries/Matter/examples/MatterTemperatureControlledCabinet/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinet/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/MatterTemperatureControlledCabinetLevels.ino b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/MatterTemperatureControlledCabinetLevels.ino new file mode 100644 index 00000000000..e246f7d8a56 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/MatterTemperatureControlledCabinetLevels.ino @@ -0,0 +1,279 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * This example demonstrates the Temperature Level mode of the Matter Temperature Controlled Cabinet Device. + * + * This example will create a Matter Device which can be commissioned and controlled from a Matter Environment APP. + * Additionally the ESP32 will send debug messages indicating the Matter activity. + * Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. + * + * The example will create a Matter Temperature Controlled Cabinet Device using temperature_level feature. + * The Temperature Controlled Cabinet can be controlled via Matter controllers to set + * temperature levels from a predefined array of supported levels. + * + * This mode is mutually exclusive with temperature_number mode. + * See MatterTemperatureControlledCabinet example for temperature setpoint control. + */ + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif + +// List of Matter Endpoints for this Node +// Matter Temperature Controlled Cabinet Endpoint +MatterTemperatureControlledCabinet TemperatureCabinet; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// set your board USER BUTTON pin here - decommissioning button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control - decommission the Matter Node +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Temperature levels array - these represent different temperature presets +// Example: 0 = Off, 1 = Low, 2 = Medium, 3 = High, 4 = Maximum +// The actual temperature values are application-specific +uint8_t supportedLevels[] = {0, 1, 2, 3, 4}; +const uint16_t levelCount = sizeof(supportedLevels) / sizeof(supportedLevels[0]); +const uint8_t initialLevel = 2; // Start with level 2 (Medium) + +// Temperature level control state +struct LevelControlState { + bool initialized; + bool increasing; + uint16_t currentLevelIndex; + uint8_t initialLevel; + bool levelReachedIncreasing; + bool levelReachedDecreasing; +}; + +static LevelControlState levelState = { + .initialized = false, .increasing = true, .currentLevelIndex = 0, .initialLevel = 0, .levelReachedIncreasing = false, .levelReachedDecreasing = false +}; + +// Initialize level control state +void initLevelControl() { + if (!levelState.initialized) { + uint8_t currentLevel = TemperatureCabinet.getSelectedTemperatureLevel(); + levelState.initialLevel = currentLevel; + // Find the index of current level in supportedLevels array + for (uint16_t i = 0; i < levelCount; i++) { + if (supportedLevels[i] == currentLevel) { + levelState.currentLevelIndex = i; + break; + } + } + levelState.initialized = true; + } +} + +// Check and log when initial level is reached/overpassed +void checkLevelReached(uint8_t newLevel, bool isIncreasing, bool directionChanged) { + if (directionChanged) { + // Reset flags when direction changes + levelState.levelReachedIncreasing = false; + levelState.levelReachedDecreasing = false; + return; + } + + if (isIncreasing && !levelState.levelReachedIncreasing && newLevel >= levelState.initialLevel) { + Serial.printf("*** Temperature level %u reached/overpassed while increasing ***\r\n", levelState.initialLevel); + levelState.levelReachedIncreasing = true; + } else if (!isIncreasing && !levelState.levelReachedDecreasing && newLevel <= levelState.initialLevel) { + Serial.printf("*** Temperature level %u reached/overpassed while decreasing ***\r\n", levelState.initialLevel); + levelState.levelReachedDecreasing = true; + } +} + +// Update temperature level with cycling logic +void updateTemperatureLevel() { + // Cycle through supported levels in both directions + bool directionChanged = false; + + if (levelState.increasing) { + levelState.currentLevelIndex++; + if (levelState.currentLevelIndex >= levelCount) { + levelState.currentLevelIndex = levelCount - 1; + levelState.increasing = false; // Reverse direction + directionChanged = true; + } + } else { + if (levelState.currentLevelIndex == 0) { + levelState.currentLevelIndex = 0; + levelState.increasing = true; // Reverse direction + directionChanged = true; + } else { + levelState.currentLevelIndex--; + } + } + + uint8_t newLevel = supportedLevels[levelState.currentLevelIndex]; + + // Check if initial level has been reached or overpassed + checkLevelReached(newLevel, levelState.increasing, directionChanged); + + // Update the temperature level + if (TemperatureCabinet.setSelectedTemperatureLevel(newLevel)) { + Serial.printf("Temperature level updated to: %u (Supported Levels: ", newLevel); + for (uint16_t i = 0; i < levelCount; i++) { + Serial.printf("%u", supportedLevels[i]); + if (i < levelCount - 1) { + Serial.print(", "); + } + } + Serial.println(")"); + } else { + Serial.printf("Failed to update temperature level to: %u\r\n", newLevel); + } +} + +// Print current level status +void printLevelStatus() { + uint8_t currentLevel = TemperatureCabinet.getSelectedTemperatureLevel(); + Serial.printf("Current Temperature Level: %u (Supported Levels: ", currentLevel); + for (uint16_t i = 0; i < levelCount; i++) { + Serial.printf("%u", supportedLevels[i]); + if (i < levelCount - 1) { + Serial.print(", "); + } + } + Serial.println(")"); +} + +// Handle button press for decommissioning +void handleButtonPress() { + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + if (digitalRead(buttonPin) == HIGH && button_state) { + button_state = false; // released + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning Temperature Controlled Cabinet Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so + } +} + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + pinMode(buttonPin, INPUT_PULLUP); + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); +#endif + + // Initialize Temperature Controlled Cabinet with temperature_level feature: + // - supportedLevels: Array of temperature level values (0-255) + // - levelCount: Number of levels in the array + // - initialLevel: Initial selected temperature level + // + // Note: This mode is mutually exclusive with temperature_number mode. + // See MatterTemperatureControlledCabinet example for temperature setpoint control. + if (!TemperatureCabinet.begin(supportedLevels, levelCount, initialLevel)) { + Serial.println("Failed to initialize Temperature Controlled Cabinet!"); + while (1) { + delay(1000); + } + } + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Temperature Controlled Cabinet Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } + + // Print initial configuration + Serial.println("\nTemperature Controlled Cabinet Configuration (Temperature Level Mode):"); + Serial.printf(" Selected Level: %u\n", TemperatureCabinet.getSelectedTemperatureLevel()); + Serial.printf(" Supported Levels Count: %u\n", TemperatureCabinet.getSupportedTemperatureLevelsCount()); + Serial.print(" Supported Levels: "); + for (uint16_t i = 0; i < levelCount; i++) { + Serial.printf("%u", supportedLevels[i]); + if (i < levelCount - 1) { + Serial.print(", "); + } + } + Serial.println(); +} + +void loop() { + static uint32_t timeCounter = 0; + static uint32_t lastUpdateTime = 0; + + // Initialize level control state on first run + initLevelControl(); + + // Update temperature level dynamically every 1 second + uint32_t currentTime = millis(); + if (currentTime - lastUpdateTime >= 1000) { // 1 second interval + lastUpdateTime = currentTime; + updateTemperatureLevel(); + } + + // Print the current temperature level every 5s + if (!(timeCounter++ % 10)) { // delaying for 500ms x 10 = 5s + printLevelStatus(); + } + + // Handle button press for decommissioning + handleButtonPress(); + + delay(500); +} diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/README.md b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/README.md new file mode 100644 index 00000000000..88f74a634bd --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/README.md @@ -0,0 +1,215 @@ +# Matter Temperature Controlled Cabinet Example (Temperature Level Mode) + +This example demonstrates how to create a Matter-compatible temperature controlled cabinet device using the **temperature_level** feature mode. This mode provides temperature control using predefined levels (e.g., Off, Low, Medium, High, Maximum) rather than precise temperature setpoint values. + +**Important:** The `temperature_number` and `temperature_level` features are **mutually exclusive**. Only one can be enabled at a time. See `MatterTemperatureControlledCabinet` example for temperature setpoint control mode. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been precompiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a temperature controlled cabinet device +- Support for both Wi-Fi and Thread(*) connectivity +- Temperature level control with array of supported levels +- Up to 16 predefined temperature levels +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Use Case + +Use this mode when you need simple preset-based temperature control (e.g., 0=Off, 1=Low, 2=Medium, 3=High, 4=Maximum) rather than precise temperature values. This is ideal for devices where users select from predefined temperature presets rather than setting exact temperatures. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect temperature control hardware (relays, heaters, coolers, etc.) to implement actual temperature control + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Temperature levels configuration** (optional): + Adjust the supported levels array and initial level in the sketch: + ```cpp + uint8_t supportedLevels[] = {0, 1, 2, 3, 4}; // Define your levels + const uint16_t levelCount = sizeof(supportedLevels) / sizeof(supportedLevels[0]); + const uint8_t initialLevel = 2; // Initial selected level + TemperatureCabinet.begin(supportedLevels, levelCount, initialLevel); + // Note: The array is copied internally, so it doesn't need to remain valid after begin() returns + ``` + +## Building and Flashing + +1. Open the `MatterTemperatureControlledCabinetLevels.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. + +Temperature Controlled Cabinet Configuration (Temperature Level Mode): + Selected Level: 2 + Supported Levels Count: 5 + Supported Levels: 0, 1, 2, 3, 4 +Temperature level updated to: 3 (Supported Levels: 0, 1, 2, 3, 4) +*** Temperature level 2 reached/overpassed while increasing *** +Temperature level updated to: 4 (Supported Levels: 0, 1, 2, 3, 4) +Temperature level updated to: 3 (Supported Levels: 0, 1, 2, 3, 4) +Temperature level updated to: 2 (Supported Levels: 0, 1, 2, 3, 4) +*** Temperature level 2 reached/overpassed while decreasing *** +Temperature level updated to: 1 (Supported Levels: 0, 1, 2, 3, 4) +... +Current Temperature Level: 2 (Supported Levels: 0, 1, 2, 3, 4) +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a temperature controlled cabinet in your Home app +7. You can select from the available temperature levels + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The temperature controlled cabinet will appear in your Alexa app +6. You can select temperature levels and set up routines + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The temperature controlled cabinet will appear in your Google Home app +7. You can select from available temperature levels + +## Code Structure + +The MatterTemperatureControlledCabinetLevels example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Temperature Controlled Cabinet endpoint with temperature level configuration, and waits for Matter commissioning. + +2. **`loop()`**: + - **Dynamic Level Updates**: Automatically cycles through all supported temperature levels every 1 second in both directions (increasing and decreasing). This demonstrates the temperature level control functionality and allows Matter controllers to observe real-time changes. + - **Level Reached Detection**: Monitors when the initial level is reached or overpassed in each direction and prints a notification message once per direction. + - Periodically prints the current temperature level (every 5 seconds) + - Handles button input for factory reset + +3. **Helper Functions**: + - `initLevelControl()`: Initializes the level control state from the current selected level + - `checkLevelReached()`: Checks and logs when the initial level is reached/overpassed + - `updateTemperatureLevel()`: Updates the temperature level with cycling logic and boundary detection + - `printLevelStatus()`: Prints the current level status + - `handleButtonPress()`: Handles button press detection and factory reset functionality + +## API Usage + +The example demonstrates the following API methods: + +- `begin(supportedLevels, levelCount, selectedLevel)` - Initialize the cabinet with temperature levels +- `getSelectedTemperatureLevel()` - Get current selected temperature level +- `setSelectedTemperatureLevel(level)` - Set selected temperature level +- `getSupportedTemperatureLevelsCount()` - Get count of supported levels +- `setSupportedTemperatureLevels(levels, count)` - Set supported temperature levels array + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Temperature level not updating**: Check Serial Monitor output to verify level changes are being processed +- **Invalid level error**: Ensure the selected level is in the supported levels array +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection +- **Wrong mode error**: Remember that temperature_number and temperature_level modes are mutually exclusive. Make sure you're using the correct example and API methods for temperature level mode + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Temperature Controlled Cabinet Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_temperature_controlled_cabinet.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/ci.yml b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterTemperatureLight/README.md b/libraries/Matter/examples/MatterTemperatureLight/README.md new file mode 100644 index 00000000000..4eb9432400b --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureLight/README.md @@ -0,0 +1,213 @@ +# Matter Color Temperature Light Example + +This example demonstrates how to create a Matter-compatible color temperature light device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, and manual control using a physical button. The color temperature light provides warm white to cool white control with adjustable brightness. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | RGB LED | Status | +| --- | ---- | ------ | ----------------- | ------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a color temperature light device +- Support for both Wi-Fi and Thread(*) connectivity +- Color temperature control (warm white to cool white, 100-500 mireds) +- Brightness level control (0-255) +- State persistence using `Preferences` library +- Button control for toggling light and factory reset +- RGB LED support with color temperature to RGB conversion +- Regular LED support with PWM brightness control +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- RGB LED connected to GPIO pins (or using built-in RGB LED), or regular LED for PWM brightness control +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **RGB LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Preferences` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in RGB LED): + ```cpp + const uint8_t ledPin = 2; // Set your RGB LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Light On/Off manual control. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterTemperatureLight.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Initial state: ON | brightness: 15 | Color Temperature: 454 mireds +Matter Node is commissioned and connected to the network. Ready for use. +Light OnOff changed to ON +Light Brightness changed to 128 +Light Color Temperature changed to 370 +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle light on/off +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Color Temperature Control + +The light supports color temperature adjustment from warm white to cool white: + +- **Warm White**: Higher mired values (400-500 mireds) - warmer, more yellow light +- **Cool White**: Lower mired values (100-200 mireds) - cooler, more blue light +- **Default**: 454 mireds (Warm White) + +The color temperature is stored in `Preferences` and restored after power cycles. + +### Brightness Control + +The light supports brightness adjustment from 0 to 255: + +- **0**: Light is off +- **1-254**: Various brightness levels +- **255**: Maximum brightness +- **Default**: 15 (~6% brightness) + +The brightness level is stored in `Preferences` and restored after power cycles. + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a color temperature light in your Home app +7. You can adjust the color temperature (warm/cool) and brightness from the Home app + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The light will appear in your Alexa app +6. You can control color temperature and brightness using voice commands or the app + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can adjust color temperature and brightness from the Google Home app + +## Code Structure + +The MatterTemperatureLight example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter Color Temperature Light endpoint, restores the last known state from `Preferences` (on/off, brightness, color temperature), and registers callbacks for state changes. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for toggling the light and factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + - `setLightState()`: Controls the physical LED. For RGB LEDs, converts color temperature (mireds) to RGB color and applies brightness. For regular LEDs, uses PWM brightness control. + - `onChangeOnOff()`: Handles on/off state changes and logs to Serial Monitor. + - `onChangeBrightness()`: Handles brightness changes and logs to Serial Monitor. + - `onChangeColorTemperature()`: Handles color temperature changes and logs to Serial Monitor. + +4. **State Persistence**: Uses `Preferences` library to store and restore: + - On/off state (default: ON if not previously stored) + - Brightness level (default: 15 if not previously stored) + - Color temperature in mireds (default: 454 mireds - Warm White if not previously stored) + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **RGB LED not responding**: Verify pin configurations and connections. For RGB LEDs, ensure the board defines `RGB_BUILTIN` or set the pin manually +- **LED not showing color temperature correctly**: RGB LED conversion uses `espCTToRgbColor()` function. For regular LEDs, only brightness is controlled via PWM +- **Color temperature not changing**: Verify that color temperature is within valid range (100-500 mireds). Check Serial Monitor for callback messages +- **Brightness not responding**: Ensure LED pin supports PWM output. Check Serial Monitor for brightness change messages +- **State not persisting**: Verify `Preferences` library is working correctly. Check that flash memory is not full +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Color Temperature Light Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_color_temperature_light.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterTemperatureLight/ci.json b/libraries/Matter/examples/MatterTemperatureLight/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterTemperatureLight/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterTemperatureLight/ci.yml b/libraries/Matter/examples/MatterTemperatureLight/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureLight/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterTemperatureSensor/README.md b/libraries/Matter/examples/MatterTemperatureSensor/README.md new file mode 100644 index 00000000000..9a494aec932 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureSensor/README.md @@ -0,0 +1,212 @@ +# Matter Temperature Sensor Example + +This example demonstrates how to create a Matter-compatible temperature sensor device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sensor data reporting to smart home ecosystems, and automatic simulation of temperature readings. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a temperature sensor device +- Support for both Wi-Fi and Thread(*) connectivity +- Temperature measurement reporting in Celsius +- Automatic simulation of temperature readings (-10°C to 10°C range) +- Periodic sensor updates every 5 seconds +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect a real temperature sensor (DS18B20, DHT22, BMP280, BME280, etc.) and replace the simulation function + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Real sensor integration** (optional): + To use a real temperature sensor, replace the `getSimulatedTemperature()` function with your sensor reading code. The function should return a float value representing temperature in Celsius. + +## Building and Flashing + +1. Open the `MatterTemperatureSensor.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +Current Temperature is -25.00C +Current Temperature is -10.00C +Current Temperature is -9.50C +... +Current Temperature is 10.00C +Current Temperature is -10.00C +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Sensor Simulation + +The example includes a simulated temperature sensor that: + +- Starts at -25°C (initial value) +- Cycles through -10°C to 10°C range +- Increases in 0.5°C steps +- Updates every 5 seconds +- Resets to -10°C when reaching 10°C + +To use a real temperature sensor, replace the `getSimulatedTemperature()` function with your sensor library code. For example, with a DS18B20: + +```cpp +#include +#include + +OneWire oneWire(4); // GPIO pin connected to DS18B20 +DallasTemperature sensors(&oneWire); + +float getSimulatedTemperature() { + sensors.requestTemperatures(); + return sensors.getTempCByIndex(0); +} +``` + +Or with a DHT22: + +```cpp +#include +DHT dht(DHT_PIN, DHT22); + +float getSimulatedTemperature() { + return dht.readTemperature(); +} +``` + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a temperature sensor in your Home app +7. You can monitor the temperature readings and set up automations based on temperature levels (e.g., turn on AC when temperature exceeds threshold) + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The temperature sensor will appear in your Alexa app +6. You can monitor temperature readings and create routines based on temperature changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can monitor temperature readings and create automations based on temperature changes + +## Code Structure + +The MatterTemperatureSensor example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Temperature Sensor endpoint with initial value (-25°C), and waits for Matter commissioning. + +2. **`loop()`**: Displays the current temperature value every 5 seconds, updates the sensor reading from the simulated hardware sensor, handles button input for factory reset, and allows the Matter stack to process events. + +3. **`getSimulatedTemperature()`**: Simulates a hardware temperature sensor by cycling through values from -10°C to 10°C in 0.5°C steps. Replace this function with your actual sensor reading code. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Temperature readings not updating**: Check that the sensor simulation function is being called correctly. For real sensors, verify sensor wiring and library initialization +- **Temperature values out of range**: Ensure temperature values are in Celsius. The Matter protocol stores values as int16_t internally (1/100th of a degree Celsius), so -273.15°C (absolute zero) to 327.67°C is the valid range +- **State not changing**: The simulated sensor increases by 0.5°C every 5 seconds. If you're using a real sensor, ensure it's properly connected and reading correctly +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Temperature Sensor Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_temperature_sensor.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterTemperatureSensor/ci.json b/libraries/Matter/examples/MatterTemperatureSensor/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterTemperatureSensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterTemperatureSensor/ci.yml b/libraries/Matter/examples/MatterTemperatureSensor/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureSensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterThermostat/README.md b/libraries/Matter/examples/MatterThermostat/README.md new file mode 100644 index 00000000000..2a87f4d93af --- /dev/null +++ b/libraries/Matter/examples/MatterThermostat/README.md @@ -0,0 +1,236 @@ +# Matter Thermostat Example + +This example demonstrates how to create a Matter-compatible thermostat device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, thermostat control via smart home ecosystems, temperature setpoint management, and simulated heating/cooling systems with automatic temperature regulation. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | Status | +| --- | ---- | ------ | ----------------- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a thermostat device +- Support for both Wi-Fi and Thread(*) connectivity +- Multiple thermostat modes: OFF, HEAT, COOL, AUTO +- Heating and cooling setpoint control +- Automatic temperature regulation in AUTO mode +- Simulated heating/cooling systems with temperature changes +- Serial input for manual temperature setting +- Button control for factory reset (decommission) +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- User button for factory reset (uses BOOT button by default) +- Optional: Connect a real temperature sensor and replace the simulation function + +## Pin Configuration + +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +3. **Real sensor integration** (optional): + To use a real temperature sensor, replace the `getSimulatedTemperature()` function with your sensor reading code. The function should return a float value representing temperature in Celsius. + +## Building and Flashing + +1. Open the `MatterThermostat.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. + +Initial Setpoints are 23.0C to 20.0C with a minimum 2.5C difference +Auto mode is ON. Initial Temperature of 12.5C +Local Temperature Sensor will be simulated every 10 seconds and changed by a simulated heater and cooler to move in between setpoints. +Current Local Temperature is 12.5C + Thermostat Mode: AUTO >>> Heater is ON -- Cooler is OFF +Current Local Temperature is 13.0C + Thermostat Mode: AUTO >>> Heater is ON -- Cooler is OFF +... +Current Local Temperature is 20.0C + Thermostat Mode: AUTO >>> Heater is OFF -- Cooler is OFF +Current Local Temperature is 23.0C + Thermostat Mode: AUTO >>> Heater is OFF -- Cooler is ON +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides factory reset functionality: + +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Serial Input + +You can manually set the temperature by typing a value in the Serial Monitor: + +1. Open Serial Monitor at 115200 baud +2. Type a temperature value between -50°C and 50°C +3. Press Enter +4. The thermostat will update the local temperature reading + +Example: +``` +15.5 +New Temperature is 15.5C +``` + +### Thermostat Modes + +The thermostat supports four operating modes: + +- **OFF**: Heating and cooling systems are turned off +- **HEAT**: Only heating system is active. Turns off when temperature exceeds heating setpoint +- **COOL**: Only cooling system is active. Turns off when temperature falls below cooling setpoint +- **AUTO**: Automatically switches between heating and cooling to maintain temperature between setpoints + +### Setpoints + +- **Heating Setpoint**: Target temperature for heating (default: 23.0°C) +- **Cooling Setpoint**: Target temperature for cooling (default: 20.0°C) +- **Deadband**: Minimum difference between heating and cooling setpoints (2.5°C required in AUTO mode) + +### Temperature Simulation + +The example includes a simulated heating/cooling system: + +- **Heating**: Temperature increases by 0.5°C every 10 seconds when heating is active +- **Cooling**: Temperature decreases by 0.5°C every 10 seconds when cooling is active +- **No heating/cooling**: Temperature remains stable + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a thermostat in your Home app +7. You can control the mode (OFF, HEAT, COOL, AUTO) and adjust heating/cooling setpoints +8. Monitor the current temperature and set up automations based on temperature + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The thermostat will appear in your Alexa app +6. You can control the mode and setpoints using voice commands or the app +7. Create routines based on temperature changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. You can control the mode and setpoints using voice commands or the app +7. Create automations based on temperature + +## Code Structure + +The MatterThermostat example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button), configures Wi-Fi (if needed), sets up the Matter Thermostat endpoint with cooling/heating sequence of operation and AUTO mode enabled, sets initial setpoints (heating: 23.0°C, cooling: 20.0°C) and initial temperature (12.5°C), and waits for Matter commissioning. + +2. **`loop()`**: Reads serial input for manual temperature setting, simulates heating/cooling systems and temperature changes every 10 seconds, controls heating/cooling based on thermostat mode and setpoints, handles button input for factory reset, and allows the Matter stack to process events. + +3. **`getSimulatedTemperature()`**: Simulates temperature changes based on heating/cooling state. Temperature increases when heating is active, decreases when cooling is active. Replace this function with your actual sensor reading code. + +4. **`readSerialForNewTemperature()`**: Reads temperature values from Serial Monitor input, validates the range (-50°C to 50°C), and updates the thermostat's local temperature. + +5. **Thermostat Control Logic**: + - **OFF mode**: Both heating and cooling are disabled + - **AUTO mode**: Automatically switches between heating and cooling to maintain temperature between setpoints + - **HEAT mode**: Activates heating until temperature exceeds heating setpoint + - **COOL mode**: Activates cooling until temperature falls below cooling setpoint + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Temperature not updating**: Check that the simulation function is being called correctly. For real sensors, verify sensor wiring and library initialization +- **Heating/cooling not responding**: Verify that the thermostat mode is set correctly and that setpoints are properly configured +- **Setpoints not working**: Ensure cooling setpoint is at least 2.5°C lower than heating setpoint in AUTO mode +- **Serial input not working**: Make sure Serial Monitor is set to 115200 baud and "No line ending" or "Newline" is selected +- **Invalid temperature values**: Temperature must be between -50°C and 50°C. The Matter protocol stores values as int16_t internally (1/100th of a degree Celsius) +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Thermostat Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_thermostat.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterThermostat/ci.json b/libraries/Matter/examples/MatterThermostat/ci.json deleted file mode 100644 index 90b393f9156..00000000000 --- a/libraries/Matter/examples/MatterThermostat/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y" - ] -} diff --git a/libraries/Matter/examples/MatterThermostat/ci.yml b/libraries/Matter/examples/MatterThermostat/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterThermostat/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterWaterFreezeDetector/MatterWaterFreezeDetector.ino b/libraries/Matter/examples/MatterWaterFreezeDetector/MatterWaterFreezeDetector.ino new file mode 100644 index 00000000000..17dbc272d7d --- /dev/null +++ b/libraries/Matter/examples/MatterWaterFreezeDetector/MatterWaterFreezeDetector.ino @@ -0,0 +1,161 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * This example is an example code that will create a Matter Device which can be + * commissioned and controlled from a Matter Environment APP. + * Additionally the ESP32 will send debug messages indicating the Matter activity. + * Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. + * + * The example will create a Matter Water Freeze Detector Device. + * The Water Freeze Detector state can be toggled by pressing the onboard button. + * The Water Freeze Detector state will be indicated by the onboard LED. + * The Water Freeze Detector state will be simulated to change every 20 seconds. + * + * The onboard button can be kept pressed for 5 seconds to decommission the Matter Node. + * The example will also show the manual commissioning code and QR code to be used in the Matter environment. + * + */ + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif + +// List of Matter Endpoints for this Node +// Matter Water Freeze Detector Endpoint +MatterWaterFreezeDetector WaterFreezeDetector; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// LED will be used to indicate the Water Freeze Detector state +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t ledPin = RGB_BUILTIN; +#else +const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here - decommissioning and Manual Water Freeze Detector toggle button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debouceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + // The button will also be used to manually toggle the Water Freeze Detector state + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the LED (light) GPIO and Matter End Point + pinMode(ledPin, OUTPUT); + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); +#endif + + // set initial water freeze detector state as false (default) + WaterFreezeDetector.begin(); + digitalWrite(ledPin, LOW); // LED OFF + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Water Freeze Detector Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } +} + +bool simulatedHWWaterFreezeDetector() { + // Simulated Water Freeze Detector + static bool freezeState = false; + static uint32_t lastTime = 0; + + // Simulate a Water Freeze Detector state change every 20 seconds + if (millis() - lastTime > 20000) { + freezeState = !freezeState; + lastTime = millis(); + } + return freezeState; +} + +void loop() { + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) { + button_state = false; // released + // button is released - toggle Freeze State (Not Detected/Detected) + WaterFreezeDetector.setFreeze(!WaterFreezeDetector.getFreeze()); // same as WaterFreezeDetector = !WaterFreezeDetector; + Serial.printf("User button released. Setting the Water Freeze Detector to %s.\r\n", WaterFreezeDetector ? "Detected" : "Not Detected"); + // LED will indicate the Water Freeze Detector state + if (WaterFreezeDetector) { + digitalWrite(ledPin, HIGH); // LED ON + } else { + digitalWrite(ledPin, LOW); // LED OFF + } + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning Water Freeze Detector Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so + } + + // Simulated Water Freeze Detector + WaterFreezeDetector.setFreeze(simulatedHWWaterFreezeDetector()); + + delay(50); +} diff --git a/libraries/Matter/examples/MatterWaterFreezeDetector/README.md b/libraries/Matter/examples/MatterWaterFreezeDetector/README.md new file mode 100644 index 00000000000..0701a5af979 --- /dev/null +++ b/libraries/Matter/examples/MatterWaterFreezeDetector/README.md @@ -0,0 +1,181 @@ +# Matter Water Freeze Detector Example + +This example demonstrates how to create a Matter-compatible water freeze detector device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, manual control using a physical button, and automatic simulation of water freeze detection state changes. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a water freeze detector device +- Support for both Wi-Fi and Thread(*) connectivity +- Water freeze detection state indication using LED (ON = Detected, OFF = Not Detected) +- Automatic simulation of water freeze detection state changes every 20 seconds +- Button control for toggling water freeze detection state and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pins (or using built-in LED) to indicate water freeze detection state +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Water Freeze Detector state toggle and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterWaterFreezeDetector.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +User button released. Setting the Water Freeze Detector to Detected. +User button released. Setting the Water Freeze Detector to Not Detected. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle water freeze detector state (Not Detected/Detected) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Automatic Simulation + +The water freeze detector state automatically toggles every 20 seconds to simulate a real water freeze detector. The LED will reflect the current state: +- **LED ON**: Water freeze is Detected +- **LED OFF**: Water freeze is Not Detected + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. +Check for Matter Water Freeze Detector endpoint support within the Matter Controller developer webpage. +This endpoint is part of the latest Matter supported device list and it may not be fully supported by your Matter environment. +You can also try the Home Assistant Matter feature in order to test it. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a water freeze detector in your Home app +7. You can monitor the water freeze detection state (Detected/Not Detected) and receive notifications when the state changes + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The water freeze detector will appear in your Alexa app +6. You can monitor the water freeze detection state and set up routines based on state changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The water freeze detector will appear in your Google Home app + +## Code Structure + +The MatterWaterFreezeDetector example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter Water Freeze Detector endpoint with initial state (Not Detected), and waits for Matter commissioning. +2. **`loop()`**: Handles button input for toggling water freeze detection state and factory reset, and automatically simulates water freeze detection state changes every 20 seconds. +3. **`simulatedHWWaterFreezeDetector()`**: Simulates a hardware water freeze detector by toggling the water freeze detection state every 20 seconds. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **Water freeze detector state not updating**: Check Serial Monitor output to verify state changes are being processed +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Water Freeze Detector Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_water_freeze_detector.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterWaterFreezeDetector/ci.yml b/libraries/Matter/examples/MatterWaterFreezeDetector/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterWaterFreezeDetector/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/examples/MatterWaterLeakDetector/MatterWaterLeakDetector.ino b/libraries/Matter/examples/MatterWaterLeakDetector/MatterWaterLeakDetector.ino new file mode 100644 index 00000000000..8cbbd586f8d --- /dev/null +++ b/libraries/Matter/examples/MatterWaterLeakDetector/MatterWaterLeakDetector.ino @@ -0,0 +1,161 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/* + * This example is an example code that will create a Matter Device which can be + * commissioned and controlled from a Matter Environment APP. + * Additionally the ESP32 will send debug messages indicating the Matter activity. + * Turning DEBUG Level ON may be useful to following Matter Accessory and Controller messages. + * + * The example will create a Matter Water Leak Detector Device. + * The Water Leak Detector state can be toggled by pressing the onboard button. + * The Water Leak Detector state will be indicated by the onboard LED. + * The Water Leak Detector state will be simulated to change every 20 seconds. + * + * The onboard button can be kept pressed for 5 seconds to decommission the Matter Node. + * The example will also show the manual commissioning code and QR code to be used in the Matter environment. + * + */ + +// Matter Manager +#include +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +#endif + +// List of Matter Endpoints for this Node +// Matter Water Leak Detector Endpoint +MatterWaterLeakDetector WaterLeakDetector; + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// WiFi is manually set and started +const char *ssid = "your-ssid"; // Change this to your WiFi SSID +const char *password = "your-password"; // Change this to your WiFi password +#endif + +// LED will be used to indicate the Water Leak Detector state +// set your board RGB LED pin here +#ifdef RGB_BUILTIN +const uint8_t ledPin = RGB_BUILTIN; +#else +const uint8_t ledPin = 2; // Set your pin here if your board has not defined LED_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +// set your board USER BUTTON pin here - decommissioning and Manual Water Leak Detector toggle button +const uint8_t buttonPin = BOOT_PIN; // Set your pin here. Using BOOT Button. + +// Button control +uint32_t button_time_stamp = 0; // debouncing control +bool button_state = false; // false = released | true = pressed +const uint32_t debounceTime = 250; // button debouncing time (ms) +const uint32_t decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +void setup() { + // Initialize the USER BUTTON (Boot button) that will be used to decommission the Matter Node + // The button will also be used to manually toggle the Water Leak Detector state + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the LED (light) GPIO and Matter End Point + pinMode(ledPin, OUTPUT); + + Serial.begin(115200); + +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE + // Manually connect to WiFi + WiFi.begin(ssid, password); + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); +#endif + + // set initial water leak detector state as false (default) + WaterLeakDetector.begin(); + digitalWrite(ledPin, LOW); // LED OFF + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Check Matter Accessory Commissioning state, which may change during execution of loop() + if (!Matter.isDeviceCommissioned()) { + Serial.println(""); + Serial.println("Matter Node is not commissioned yet."); + Serial.println("Initiate the device discovery in your Matter environment."); + Serial.println("Commission it to your Matter hub with the manual pairing code or QR code"); + Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str()); + Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str()); + // waits for Matter Water Leak Detector Commissioning. + uint32_t timeCount = 0; + while (!Matter.isDeviceCommissioned()) { + delay(100); + if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec + Serial.println("Matter Node not commissioned yet. Waiting for commissioning."); + } + } + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } +} + +bool simulatedHWWaterLeakDetector() { + // Simulated Water Leak Detector + static bool leakState = false; + static uint32_t lastTime = 0; + + // Simulate a Water Leak Detector state change every 20 seconds + if (millis() - lastTime > 20000) { + leakState = !leakState; + lastTime = millis(); + } + return leakState; +} + +void loop() { + // Check if the button has been pressed + if (digitalRead(buttonPin) == LOW && !button_state) { + // deals with button debouncing + button_time_stamp = millis(); // record the time while the button is pressed. + button_state = true; // pressed. + } + + uint32_t time_diff = millis() - button_time_stamp; + if (button_state && time_diff > debounceTime && digitalRead(buttonPin) == HIGH) { + button_state = false; // released + // button is released - toggle Leak State (Not Detected/Detected) + WaterLeakDetector.setLeak(!WaterLeakDetector.getLeak()); // same as WaterLeakDetector = !WaterLeakDetector; + Serial.printf("User button released. Setting the Water Leak Detector to %s.\r\n", WaterLeakDetector ? "Detected" : "Not Detected"); + // LED will indicate the Water Leak Detector state + if (WaterLeakDetector) { + digitalWrite(ledPin, HIGH); // LED ON + } else { + digitalWrite(ledPin, LOW); // LED OFF + } + } + + // Onboard User Button is kept pressed for longer than 5 seconds in order to decommission matter node + if (button_state && time_diff > decommissioningTimeout) { + Serial.println("Decommissioning Water Leak Detector Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so + } + + // Simulated Water Leak Detector + WaterLeakDetector.setLeak(simulatedHWWaterLeakDetector()); + + delay(50); +} diff --git a/libraries/Matter/examples/MatterWaterLeakDetector/README.md b/libraries/Matter/examples/MatterWaterLeakDetector/README.md new file mode 100644 index 00000000000..3dcf2f2e9f7 --- /dev/null +++ b/libraries/Matter/examples/MatterWaterLeakDetector/README.md @@ -0,0 +1,181 @@ +# Matter Water Leak Detector Example + +This example demonstrates how to create a Matter-compatible water leak detector device using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, device control via smart home ecosystems, manual control using a physical button, and automatic simulation of water leak detection state changes. + +## Supported Targets + +| SoC | Wi-Fi | Thread | BLE Commissioning | LED | Status | +| --- | ---- | ------ | ----------------- | --- | ------ | +| ESP32 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S2 | ✅ | ❌ | ❌ | Required | Fully supported | +| ESP32-S3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C3 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | ❌ | ✅ | Required | Fully supported | +| ESP32-H2 | ❌ | ✅ | ✅ | Required | Supported (Thread only) | + +### Note on Commissioning: + +- **ESP32 & ESP32-S2** do not support commissioning over Bluetooth LE. For these chips, you must provide Wi-Fi credentials directly in the sketch code so they can connect to your network manually. +- **ESP32-C6** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. +- **ESP32-C5** Although it has Thread support, the ESP32 Arduino Matter Library has been pre compiled using Wi-Fi only. In order to configure it for Thread-only operation it is necessary to build the project as an ESP-IDF component and to disable the Matter Wi-Fi station feature. + +## Features + +- Matter protocol implementation for a water leak detector device +- Support for both Wi-Fi and Thread(*) connectivity +- Water leak detection state indication using LED (ON = Detected, OFF = Not Detected) +- Automatic simulation of water leak detection state changes every 20 seconds +- Button control for toggling water leak detection state and factory reset +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home +(*) It is necessary to compile the project using Arduino as IDF Component. + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pins (or using built-in LED) to indicate water leak detection state +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **LED**: Uses `RGB_BUILTIN` if defined, otherwise pin 2 +- **Button**: Uses `BOOT_PIN` by default + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with Matter support +3. ESP32 Arduino libraries: + - `Matter` + - `Wi-Fi` (only for ESP32 and ESP32-S2) + +### Configuration + +Before uploading the sketch, configure the following: + +1. **Wi-Fi credentials** (if not using BLE commissioning - mandatory for ESP32 | ESP32-S2): + ```cpp + const char *ssid = "your-ssid"; // Change to your Wi-Fi SSID + const char *password = "your-password"; // Change to your Wi-Fi password + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +3. **Button pin configuration** (optional): + By default, the `BOOT` button (GPIO 0) is used for the Water Leak Detector state toggle and factory reset. You can change this to a different pin if needed. + ```cpp + const uint8_t buttonPin = BOOT_PIN; // Set your button pin here + ``` + +## Building and Flashing + +1. Open the `MatterWaterLeakDetector.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. The Wi-Fi connection messages will be displayed only for ESP32 and ESP32-S2. Other targets will use Matter CHIPoBLE to automatically setup the IP Network. You should see output similar to the following, which provides the necessary information for commissioning: + +``` +Connecting to your-wifi-ssid +....... +Wi-Fi connected +IP address: 192.168.1.100 + +Matter Node is not commissioned yet. +Initiate the device discovery in your Matter environment. +Commission it to your Matter hub with the manual pairing code or QR code +Manual pairing code: 34970112332 +QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A6FCJ142C00KA0648G00 +Matter Node not commissioned yet. Waiting for commissioning. +Matter Node not commissioned yet. Waiting for commissioning. +... +Matter Node is commissioned and connected to the network. Ready for use. +User button released. Setting the Water Leak Detector to Detected. +User button released. Setting the Water Leak Detector to Not Detected. +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Toggle water leak detector state (Not Detected/Detected) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### Automatic Simulation + +The water leak detector state automatically toggles every 20 seconds to simulate a real water leak detector. The LED will reflect the current state: +- **LED ON**: Water leak is Detected +- **LED OFF**: Water leak is Not Detected + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. +Check for Matter Water Leak Detector endpoint support within the Matter Controller developer webpage. +This endpoint is part of the latest Matter supported device list and it may not be fully supported by your Matter environment. +You can also try the Home Assistant Matter feature in order to test it. + +#### Apple Home + +1. Open the Home app on your iOS device +2. Tap the "+" button > Add Accessory +3. Scan the QR code displayed in the Serial Monitor, or +4. Tap "I Don't Have a Code or Cannot Scan" and enter the manual pairing code +5. Follow the prompts to complete setup +6. The device will appear as a water leak detector in your Home app +7. You can monitor the water leak detection state (Detected/Not Detected) and receive notifications when the state changes + +#### Amazon Alexa + +1. Open the Alexa app +2. Tap More > Add Device > Matter +3. Select "Scan QR code" or "Enter code manually" +4. Complete the setup process +5. The water leak detector will appear in your Alexa app +6. You can monitor the water leak detection state and set up routines based on state changes + +#### Google Home + +1. Open the Google Home app +2. Tap "+" > Set up device > New device +3. Choose "Matter device" +4. Scan the QR code or enter the manual pairing code +5. Follow the prompts to complete setup +6. The water leak detector will appear in your Google Home app + +## Code Structure + +The MatterWaterLeakDetector example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, LED), configures Wi-Fi (if needed), sets up the Matter Water Leak Detector endpoint with initial state (Not Detected), and waits for Matter commissioning. +2. **`loop()`**: Handles button input for toggling water leak detection state and factory reset, and automatically simulates water leak detection state changes every 20 seconds. +3. **`simulatedHWWaterLeakDetector()`**: Simulates a hardware water leak detector by toggling the water leak detection state every 20 seconds. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **LED not responding**: Verify pin configurations and connections +- **Water leak detector state not updating**: Check Serial Monitor output to verify state changes are being processed +- **Failed to commission**: Try factory resetting the device by long-pressing the button. Other option would be to erase the SoC Flash Memory by using `Arduino IDE Menu` -> `Tools` -> `Erase All Flash Before Sketch Upload: "Enabled"` or directly with `esptool.py --port erase_flash` +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [Matter Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter.html) +- [Matter Endpoint Base Class](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/matter_ep.html) +- [Matter Water Leak Detector Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_water_leak_detector.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterWaterLeakDetector/ci.yml b/libraries/Matter/examples/MatterWaterLeakDetector/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterWaterLeakDetector/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_ESP_MATTER_ENABLE_DATA_MODEL=y diff --git a/libraries/Matter/keywords.txt b/libraries/Matter/keywords.txt index 6c2e092e417..cc592f99da6 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -19,11 +19,16 @@ MatterFan KEYWORD1 FanMode_t KEYWORD1 FanModeSequence_t KEYWORD1 MatterTemperatureSensor KEYWORD1 +MatterTemperatureControlledCabinet KEYWORD1 MatterHumiditySensor KEYWORD1 MatterContactSensor KEYWORD1 +MatterWaterLeakDetector KEYWORD1 +MatterWaterFreezeDetector KEYWORD1 +MatterRainSensor KEYWORD1 MatterPressureSensor KEYWORD1 MatterOccupancySensor KEYWORD1 MatterOnOffPlugin KEYWORD1 +MatterDimmablePlugin KEYWORD1 MatterThermostat KEYWORD1 ControlSequenceOfOperation_t KEYWORD1 ThermostatMode_t KEYWORD1 @@ -60,6 +65,8 @@ getOnOff KEYWORD2 toggle KEYWORD2 setBrightness KEYWORD2 getBrightness KEYWORD2 +setLevel KEYWORD2 +getLevel KEYWORD2 setColorTemperature KEYWORD2 getColorTemperature KEYWORD2 setColorRGB KEYWORD2 @@ -86,10 +93,28 @@ onChangeMode KEYWORD2 onChangeSpeedPercent KEYWORD2 setTemperature KEYWORD2 getTemperature KEYWORD2 +setTemperatureSetpoint KEYWORD2 +getTemperatureSetpoint KEYWORD2 +setMinTemperature KEYWORD2 +getMinTemperature KEYWORD2 +setMaxTemperature KEYWORD2 +getMaxTemperature KEYWORD2 +setStep KEYWORD2 +getStep KEYWORD2 +setSelectedTemperatureLevel KEYWORD2 +getSelectedTemperatureLevel KEYWORD2 +setSupportedTemperatureLevels KEYWORD2 +getSupportedTemperatureLevelsCount KEYWORD2 setHumidity KEYWORD2 getHumidity KEYWORD2 setContact KEYWORD2 getContact KEYWORD2 +setLeak KEYWORD2 +getLeak KEYWORD2 +setFreeze KEYWORD2 +getFreeze KEYWORD2 +setRain KEYWORD2 +getRain KEYWORD2 setPressure KEYWORD2 getPressure KEYWORD2 setOccupancy KEYWORD2 @@ -108,7 +133,6 @@ setCoolingHeatingSetpoints KEYWORD2 setLocalTemperature KEYWORD2 getLocalTemperature KEYWORD2 getThermostatModeString KEYWORD2 -onChangeMode KEYWORD2 onChangeLocalTemperature KEYWORD2 onChangeCoolingSetpoint KEYWORD2 onChangeHeatingSetpoint KEYWORD2 diff --git a/libraries/Matter/library.properties b/libraries/Matter/library.properties index 0b140bfa169..b95e4e09a10 100644 --- a/libraries/Matter/library.properties +++ b/libraries/Matter/library.properties @@ -1,5 +1,5 @@ name=Matter -version=3.3.0 +version=3.3.4 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for supporting Matter environment on ESP32. diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index 09e59b4e04b..a1c888e1792 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -27,11 +27,16 @@ #include #include #include +#include #include #include +#include +#include +#include #include #include #include +#include #include // Matter Event types used when there is a user callback for Matter Events @@ -187,13 +192,18 @@ class ArduinoMatter { friend class MatterGenericSwitch; friend class MatterOnOffLight; friend class MatterDimmableLight; + friend class MatterDimmablePlugin; friend class MatterColorTemperatureLight; friend class MatterColorLight; friend class MatterEnhancedColorLight; friend class MatterFan; friend class MatterTemperatureSensor; + friend class MatterTemperatureControlledCabinet; friend class MatterHumiditySensor; friend class MatterContactSensor; + friend class MatterWaterLeakDetector; + friend class MatterWaterFreezeDetector; + friend class MatterRainSensor; friend class MatterPressureSensor; friend class MatterOccupancySensor; friend class MatterOnOffPlugin; @@ -203,6 +213,8 @@ class ArduinoMatter { static void _init(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MATTER) extern ArduinoMatter Matter; +#endif #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndPoint.cpp b/libraries/Matter/src/MatterEndPoint.cpp index ecf1acff579..8ef1c5d63ba 100644 --- a/libraries/Matter/src/MatterEndPoint.cpp +++ b/libraries/Matter/src/MatterEndPoint.cpp @@ -27,6 +27,8 @@ bool MatterEndPoint::createSecondaryNetworkInterface() { log_v("Secondary network interface endpoint already exists with ID %d", secondary_network_endpoint_id); return false; } + +#if CHIP_DEVICE_CONFIG_ENABLE_THREAD && CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION // Create a secondary network interface endpoint endpoint::secondary_network_interface::config_t secondary_network_interface_config; secondary_network_interface_config.network_commissioning.feature_map = chip::to_underlying( @@ -40,6 +42,11 @@ bool MatterEndPoint::createSecondaryNetworkInterface() { } secondary_network_endpoint_id = endpoint::get_id(endpoint); log_i("Secondary Network Interface created with endpoint_id %d", secondary_network_endpoint_id); +#else + log_i("Secondary Network Interface not supported"); + return false; +#endif + return true; } diff --git a/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp b/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp new file mode 100644 index 00000000000..8e7318cf577 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.cpp @@ -0,0 +1,194 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +bool MatterDimmablePlugin::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter DimmablePlugin device has not begun."); + return false; + } + + log_d("DimmablePlugin Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + + if (endpoint_id == getEndPointId()) { + switch (cluster_id) { + case OnOff::Id: + if (attribute_id == OnOff::Attributes::OnOff::Id) { + log_d("DimmablePlugin On/Off State changed to %d", val->val.b); + if (_onChangeOnOffCB != NULL) { + ret &= _onChangeOnOffCB(val->val.b); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(val->val.b, level); + } + if (ret == true) { + onOffState = val->val.b; + } + } + break; + case LevelControl::Id: + if (attribute_id == LevelControl::Attributes::CurrentLevel::Id) { + log_d("DimmablePlugin Level changed to %d", val->val.u8); + if (_onChangeLevelCB != NULL) { + ret &= _onChangeLevelCB(val->val.u8); + } + if (_onChangeCB != NULL) { + ret &= _onChangeCB(onOffState, val->val.u8); + } + if (ret == true) { + level = val->val.u8; + } + } + break; + } + } + return ret; +} + +MatterDimmablePlugin::MatterDimmablePlugin() {} + +MatterDimmablePlugin::~MatterDimmablePlugin() { + end(); +} + +bool MatterDimmablePlugin::begin(bool initialState, uint8_t level) { + ArduinoMatter::_init(); + if (getEndPointId() != 0) { + log_e("Matter Dimmable Plugin with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + dimmable_plugin_unit::config_t plugin_config; + plugin_config.on_off.on_off = initialState; + plugin_config.on_off.lighting.start_up_on_off = nullptr; + onOffState = initialState; + + plugin_config.level_control.current_level = level; + plugin_config.level_control.lighting.start_up_current_level = nullptr; + this->level = level; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = dimmable_plugin_unit::create(node::get(), &plugin_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create dimmable plugin endpoint"); + return false; + } + + setEndPointId(endpoint::get_id(endpoint)); + log_i("Dimmable Plugin created with endpoint_id %d", getEndPointId()); + + /* Mark deferred persistence for some attributes that might be changed rapidly */ + cluster_t *level_control_cluster = cluster::get(endpoint, LevelControl::Id); + esp_matter::attribute_t *current_level_attribute = attribute::get(level_control_cluster, LevelControl::Attributes::CurrentLevel::Id); + attribute::set_deferred_persistence(current_level_attribute); + + started = true; + return true; +} + +void MatterDimmablePlugin::end() { + started = false; +} + +bool MatterDimmablePlugin::setOnOff(bool newState) { + if (!started) { + log_e("Matter Dimmable Plugin device has not begun."); + return false; + } + + // avoid processing if there was no change + if (onOffState == newState) { + return true; + } + + onOffState = newState; + + endpoint_t *endpoint = endpoint::get(node::get(), getEndPointId()); + cluster_t *cluster = cluster::get(endpoint, OnOff::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, OnOff::Attributes::OnOff::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + + if (val.val.b != onOffState) { + val.val.b = onOffState; + attribute::update(getEndPointId(), OnOff::Id, OnOff::Attributes::OnOff::Id, &val); + } + return true; +} + +void MatterDimmablePlugin::updateAccessory() { + if (_onChangeCB != NULL) { + _onChangeCB(onOffState, level); + } +} + +bool MatterDimmablePlugin::getOnOff() { + return onOffState; +} + +bool MatterDimmablePlugin::toggle() { + return setOnOff(!onOffState); +} + +bool MatterDimmablePlugin::setLevel(uint8_t newLevel) { + if (!started) { + log_w("Matter Dimmable Plugin device has not begun."); + return false; + } + + // avoid processing if there was no change + if (level == newLevel) { + return true; + } + + level = newLevel; + + endpoint_t *endpoint = endpoint::get(node::get(), getEndPointId()); + cluster_t *cluster = cluster::get(endpoint, LevelControl::Id); + esp_matter::attribute_t *attribute = attribute::get(cluster, LevelControl::Attributes::CurrentLevel::Id); + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + attribute::get_val(attribute, &val); + + if (val.val.u8 != level) { + val.val.u8 = level; + attribute::update(getEndPointId(), LevelControl::Id, LevelControl::Attributes::CurrentLevel::Id, &val); + } + return true; +} + +uint8_t MatterDimmablePlugin::getLevel() { + return level; +} + +MatterDimmablePlugin::operator bool() { + return getOnOff(); +} + +void MatterDimmablePlugin::operator=(bool newState) { + setOnOff(newState); +} +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.h b/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.h new file mode 100644 index 00000000000..1fb4e32c3a9 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterDimmablePlugin.h @@ -0,0 +1,75 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterDimmablePlugin : public MatterEndPoint { +public: + static const uint8_t MAX_LEVEL = 255; + + MatterDimmablePlugin(); + ~MatterDimmablePlugin(); + // default initial state is off and level is 64 (25%) + virtual bool begin(bool initialState = false, uint8_t level = 64); + // this will just stop processing Plugin Matter events + void end(); + + bool setOnOff(bool newState); // returns true if successful + bool getOnOff(); // returns current plugin state + bool toggle(); // returns true if successful + + bool setLevel(uint8_t newLevel); // returns true if successful + uint8_t getLevel(); // returns current level + + // User Callback for whenever the Plugin On/Off state is changed by the Matter Controller + using EndPointOnOffCB = std::function; + void onChangeOnOff(EndPointOnOffCB onChangeCB) { + _onChangeOnOffCB = onChangeCB; + } + + // User Callback for whenever the Plugin level value [0..255] is changed by the Matter Controller + using EndPointLevelCB = std::function; + void onChangeLevel(EndPointLevelCB onChangeCB) { + _onChangeLevelCB = onChangeCB; + } + + // User Callback for whenever any parameter is changed by the Matter Controller + using EndPointCB = std::function; + void onChange(EndPointCB onChangeCB) { + _onChangeCB = onChangeCB; + } + + // used to update the state of the plugin using the current Matter Plugin internal state + // It is necessary to set a user callback function using onChange() to handle the physical plugin state + void updateAccessory(); + + operator bool(); // returns current on/off plugin state + void operator=(bool state); // turns plugin on or off + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + bool onOffState = false; // default initial state is off, but it can be changed by begin(bool) + uint8_t level = 0; // default initial level is 0, but it can be changed by begin(bool, uint8_t) + EndPointOnOffCB _onChangeOnOffCB = NULL; + EndPointLevelCB _onChangeLevelCB = NULL; + EndPointCB _onChangeCB = NULL; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp b/libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp new file mode 100644 index 00000000000..76fb4929ade --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterRainSensor.cpp @@ -0,0 +1,104 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +bool MatterRainSensor::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter Rain Sensor device has not begun."); + return false; + } + + log_d("Rain Sensor Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + return ret; +} + +MatterRainSensor::MatterRainSensor() {} + +MatterRainSensor::~MatterRainSensor() { + end(); +} + +bool MatterRainSensor::begin(bool _rainState) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Matter Rain Sensor with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + rain_sensor::config_t rain_sensor_config; + rain_sensor_config.boolean_state.state_value = _rainState; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = rain_sensor::create(node::get(), &rain_sensor_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Rain Sensor endpoint"); + return false; + } + rainState = _rainState; + setEndPointId(endpoint::get_id(endpoint)); + log_i("Rain Sensor created with endpoint_id %d", getEndPointId()); + + started = true; + return true; +} + +void MatterRainSensor::end() { + started = false; +} + +bool MatterRainSensor::setRain(bool _rainState) { + if (!started) { + log_e("Matter Rain Sensor device has not begun."); + return false; + } + + // avoid processing if there was no change + if (rainState == _rainState) { + return true; + } + + esp_matter_attr_val_t rainVal = esp_matter_invalid(NULL); + + if (!getAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &rainVal)) { + log_e("Failed to get Rain Sensor Attribute."); + return false; + } + if (rainVal.val.u8 != _rainState) { + rainVal.val.u8 = _rainState; + bool ret; + ret = updateAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &rainVal); + if (!ret) { + log_e("Failed to update Rain Sensor Attribute."); + return false; + } + rainState = _rainState; + } + log_v("Rain Sensor set to %s", _rainState ? "Detected" : "Not Detected"); + + return true; +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterRainSensor.h b/libraries/Matter/src/MatterEndpoints/MatterRainSensor.h new file mode 100644 index 00000000000..d19d7feda34 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterRainSensor.h @@ -0,0 +1,54 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterRainSensor : public MatterEndPoint { +public: + MatterRainSensor(); + ~MatterRainSensor(); + // begin Matter Rain Sensor endpoint with initial rain state + bool begin(bool _rainState = false); + // this will just stop processing Rain Sensor Matter events + void end(); + + // set the rain state + bool setRain(bool _rainState); + // returns the rain state + bool getRain() { + return rainState; + } + + // bool conversion operator + void operator=(bool _rainState) { + setRain(_rainState); + } + // bool conversion operator + operator bool() { + return getRain(); + } + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + bool rainState = false; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp b/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp new file mode 100644 index 00000000000..3e0545af141 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.cpp @@ -0,0 +1,672 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace esp_matter::cluster; +using namespace chip::app::Clusters; + +// Custom endpoint for temperature_level_controlled_cabinet device +// This endpoint uses temperature_level feature instead of temperature_number +namespace esp_matter { +using namespace cluster; +namespace endpoint { +namespace temperature_level_controlled_cabinet { +typedef struct config { + cluster::descriptor::config_t descriptor; + cluster::temperature_control::config_t temperature_control; +} config_t; + +uint32_t get_device_type_id() { + return ESP_MATTER_TEMPERATURE_CONTROLLED_CABINET_DEVICE_TYPE_ID; +} + +uint8_t get_device_type_version() { + return ESP_MATTER_TEMPERATURE_CONTROLLED_CABINET_DEVICE_TYPE_VERSION; +} + +esp_err_t add(endpoint_t *endpoint, config_t *config) { + if (!endpoint) { + log_e("Endpoint cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + esp_err_t err = add_device_type(endpoint, get_device_type_id(), get_device_type_version()); + if (err != ESP_OK) { + log_e("Failed to add device type id:%" PRIu32 ",err: %d", get_device_type_id(), err); + return err; + } + + // Create temperature_control cluster with temperature_level feature + // Note: temperature_number and temperature_level are mutually exclusive + temperature_control::create(endpoint, &(config->temperature_control), CLUSTER_FLAG_SERVER, temperature_control::feature::temperature_level::get_id()); + + return ESP_OK; +} + +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) { + endpoint_t *endpoint = endpoint::create(node, flags, priv_data); + add(endpoint, config); + return endpoint; +} +} // namespace temperature_level_controlled_cabinet +} // namespace endpoint +} // namespace esp_matter + +bool MatterTemperatureControlledCabinet::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + log_d("Temperature Controlled Cabinet Attr update callback: endpoint: %u, cluster: %u, attribute: %u", endpoint_id, cluster_id, attribute_id); + + // Handle TemperatureControl cluster attribute changes from Matter controller + if (cluster_id == TemperatureControl::Id) { + switch (attribute_id) { + case TemperatureControl::Attributes::TemperatureSetpoint::Id: + if (useTemperatureNumber) { + rawTempSetpoint = val->val.i16; + log_i("Temperature setpoint changed to %.02fC", (float)rawTempSetpoint / 100.0); + } else { + log_w("Temperature setpoint change ignored - temperature_level feature is active"); + } + break; + + case TemperatureControl::Attributes::MinTemperature::Id: + if (useTemperatureNumber) { + rawMinTemperature = val->val.i16; + log_i("Min temperature changed to %.02fC", (float)rawMinTemperature / 100.0); + } else { + log_w("Min temperature change ignored - temperature_level feature is active"); + } + break; + + case TemperatureControl::Attributes::MaxTemperature::Id: + if (useTemperatureNumber) { + rawMaxTemperature = val->val.i16; + log_i("Max temperature changed to %.02fC", (float)rawMaxTemperature / 100.0); + } else { + log_w("Max temperature change ignored - temperature_level feature is active"); + } + break; + + case TemperatureControl::Attributes::Step::Id: + if (useTemperatureNumber) { + rawStep = val->val.i16; + log_i("Temperature step changed to %.02fC", (float)rawStep / 100.0); + } else { + log_w("Temperature step change ignored - temperature_level feature is active"); + } + break; + + case TemperatureControl::Attributes::SelectedTemperatureLevel::Id: + if (!useTemperatureNumber) { + selectedTempLevel = val->val.u8; + log_i("Selected temperature level changed to %u", selectedTempLevel); + } else { + log_w("Selected temperature level change ignored - temperature_number feature is active"); + } + break; + + case TemperatureControl::Attributes::SupportedTemperatureLevels::Id: + // SupportedTemperatureLevels is read-only (managed via iterator delegate) + // But if a controller tries to write it, we should log it + log_w("SupportedTemperatureLevels change attempted - this attribute is read-only"); + break; + + default: log_d("Unhandled TemperatureControl Attribute ID: %u", attribute_id); break; + } + } + + return ret; +} + +MatterTemperatureControlledCabinet::MatterTemperatureControlledCabinet() {} + +MatterTemperatureControlledCabinet::~MatterTemperatureControlledCabinet() { + end(); +} + +bool MatterTemperatureControlledCabinet::begin(double tempSetpoint, double minTemperature, double maxTemperature, double step) { + int16_t rawSetpoint = static_cast(tempSetpoint * 100.0); + int16_t rawMin = static_cast(minTemperature * 100.0); + int16_t rawMax = static_cast(maxTemperature * 100.0); + int16_t rawStepValue = static_cast(step * 100.0); + return begin(rawSetpoint, rawMin, rawMax, rawStepValue); +} + +bool MatterTemperatureControlledCabinet::begin(int16_t _rawTempSetpoint, int16_t _rawMinTemperature, int16_t _rawMaxTemperature, int16_t _rawStep) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Temperature Controlled Cabinet with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + // Note: esp-matter automatically creates all attributes from the config struct when features are enabled + // - temperature_number feature creates: TemperatureSetpoint, MinTemperature, MaxTemperature + // - temperature_step feature creates: Step (always enabled for temperature_number mode to allow setStep() later) + // No need to manually set attributes here as they are already created with the config values + + temperature_controlled_cabinet::config_t cabinet_config; + cabinet_config.temperature_control.temperature_number.temp_setpoint = _rawTempSetpoint; + cabinet_config.temperature_control.temperature_number.min_temperature = _rawMinTemperature; + cabinet_config.temperature_control.temperature_number.max_temperature = _rawMaxTemperature; + cabinet_config.temperature_control.temperature_step.step = _rawStep; + cabinet_config.temperature_control.temperature_level.selected_temp_level = 0; + + // Enable temperature_number feature (required) + // Note: temperature_number and temperature_level are mutually exclusive. + // Only one of them can be enabled at a time. + cabinet_config.temperature_control.features = temperature_control::feature::temperature_number::get_id(); + + // Always enable temperature_step feature to allow setStep() to be called later + // Note: temperature_step requires temperature_number feature (which is always enabled for this mode) + // The step value can be set initially via begin() or later via setStep() + cabinet_config.temperature_control.features |= temperature_control::feature::temperature_step::get_id(); + + // endpoint handles can be used to add/modify clusters + endpoint_t *endpoint = temperature_controlled_cabinet::create(node::get(), &cabinet_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Temperature Controlled Cabinet endpoint"); + return false; + } + + rawTempSetpoint = _rawTempSetpoint; + rawMinTemperature = _rawMinTemperature; + rawMaxTemperature = _rawMaxTemperature; + rawStep = _rawStep; + selectedTempLevel = 0; + useTemperatureNumber = true; // Set feature mode to temperature_number + + setEndPointId(endpoint::get_id(endpoint)); + log_i("Temperature Controlled Cabinet created with temperature_number feature, endpoint_id %d", getEndPointId()); + + // Workaround: Manually create Step attribute if it wasn't created automatically + // This handles the case where temperature_step::add() fails due to feature map timing issue + // The feature map check in temperature_step::add() may not see the temperature_number feature + // immediately after it's added, causing the Step attribute to not be created + cluster_t *cluster = cluster::get(endpoint, TemperatureControl::Id); + if (cluster != nullptr) { + attribute_t *step_attr = attribute::get(cluster, TemperatureControl::Attributes::Step::Id); + if (step_attr == nullptr) { + // Step attribute wasn't created, manually create it + log_w("Step attribute not found after endpoint creation, manually creating it"); + step_attr = temperature_control::attribute::create_step(cluster, _rawStep); + if (step_attr != nullptr) { + // Update the feature map to include temperature_step feature + // This ensures the feature is properly registered even though the attribute was created manually + esp_matter_attr_val_t feature_map_val = esp_matter_invalid(NULL); + attribute_t *feature_map_attr = attribute::get(cluster, Globals::Attributes::FeatureMap::Id); + if (feature_map_attr != nullptr && attribute::get_val(feature_map_attr, &feature_map_val) == ESP_OK) { + feature_map_val.val.u32 |= temperature_control::feature::temperature_step::get_id(); + attribute::set_val(feature_map_attr, &feature_map_val); + } + log_i("Step attribute manually created with value %.02fC", (float)_rawStep / 100.0); + } else { + log_e("Failed to manually create Step attribute"); + } + } + } + + started = true; + return true; +} + +bool MatterTemperatureControlledCabinet::begin(uint8_t *supportedLevels, uint16_t levelCount, uint8_t selectedLevel) { + if (supportedLevels == nullptr || levelCount == 0) { + log_e("Invalid supportedLevels array or levelCount. Must provide at least one level."); + return false; + } + + // Validate against maximum from ESP-Matter + if (levelCount > temperature_control::k_max_temp_level_count) { + log_e("Level count %u exceeds maximum %u", levelCount, temperature_control::k_max_temp_level_count); + return false; + } + + // Validate that selectedLevel exists in supportedLevels array + bool levelFound = false; + for (uint16_t i = 0; i < levelCount; i++) { + if (supportedLevels[i] == selectedLevel) { + levelFound = true; + break; + } + } + if (!levelFound) { + log_e("Selected level %u is not in the supported levels array", selectedLevel); + return false; + } + return beginInternal(supportedLevels, levelCount, selectedLevel); +} + +bool MatterTemperatureControlledCabinet::beginInternal(uint8_t *supportedLevels, uint16_t levelCount, uint8_t selectedLevel) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Temperature Controlled Cabinet with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + // Use custom temperature_level_controlled_cabinet endpoint that supports temperature_level feature + temperature_level_controlled_cabinet::config_t cabinet_config; + // Initialize temperature_number config (not used but required for struct) + cabinet_config.temperature_control.temperature_number.temp_setpoint = 0; + cabinet_config.temperature_control.temperature_number.min_temperature = 0; + cabinet_config.temperature_control.temperature_number.max_temperature = 0; + cabinet_config.temperature_control.temperature_step.step = 0; + cabinet_config.temperature_control.temperature_level.selected_temp_level = selectedLevel; + + // Enable temperature_level feature + // Note: temperature_number and temperature_level are mutually exclusive. + // Only one of them can be enabled at a time. + cabinet_config.temperature_control.features = temperature_control::feature::temperature_level::get_id(); + + // endpoint handles can be used to add/modify clusters + endpoint_t *endpoint = temperature_level_controlled_cabinet::create(node::get(), &cabinet_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Temperature Level Controlled Cabinet endpoint"); + return false; + } + + // Copy supported levels array into internal buffer + memcpy(supportedLevelsArray, supportedLevels, levelCount * sizeof(uint8_t)); + supportedLevelsCount = levelCount; + selectedTempLevel = selectedLevel; + useTemperatureNumber = false; // Set feature mode to temperature_level + + // Initialize temperature_number values to 0 (not used in this mode) + rawTempSetpoint = 0; + rawMinTemperature = 0; + rawMaxTemperature = 0; + rawStep = 0; + + setEndPointId(endpoint::get_id(endpoint)); + log_i("Temperature Level Controlled Cabinet created with temperature_level feature, endpoint_id %d", getEndPointId()); + + // Set started flag before calling setter methods (they check for started) + started = true; + + // Set supported temperature levels using internal copy + if (!setSupportedTemperatureLevels(supportedLevelsArray, levelCount)) { + log_e("Failed to set supported temperature levels"); + started = false; // Reset on failure + return false; + } + + // Set selected temperature level + if (!setSelectedTemperatureLevel(selectedLevel)) { + log_e("Failed to set selected temperature level"); + started = false; // Reset on failure + return false; + } + + return true; +} + +void MatterTemperatureControlledCabinet::end() { + started = false; + useTemperatureNumber = true; // Reset to default + supportedLevelsCount = 0; + // No need to clear array - it's a fixed-size buffer +} + +bool MatterTemperatureControlledCabinet::setRawTemperatureSetpoint(int16_t _rawTemperature) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (!useTemperatureNumber) { + log_e("Temperature setpoint methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return false; + } + + // Validate against min/max + if (_rawTemperature < rawMinTemperature || _rawTemperature > rawMaxTemperature) { + log_e( + "Temperature setpoint %.02fC is out of range [%.02fC, %.02fC]", (float)_rawTemperature / 100.0, (float)rawMinTemperature / 100.0, + (float)rawMaxTemperature / 100.0 + ); + return false; + } + + // avoid processing if there was no change + if (rawTempSetpoint == _rawTemperature) { + return true; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (!getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::TemperatureSetpoint::Id, &tempVal)) { + log_e("Failed to get Temperature Controlled Cabinet Temperature Setpoint Attribute."); + return false; + } + if (tempVal.val.i16 != _rawTemperature) { + tempVal.val.i16 = _rawTemperature; + bool ret; + ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::TemperatureSetpoint::Id, &tempVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Temperature Setpoint Attribute."); + return false; + } + rawTempSetpoint = _rawTemperature; + } + log_v("Temperature Controlled Cabinet setpoint set to %.02fC", (float)_rawTemperature / 100.00); + + return true; +} + +bool MatterTemperatureControlledCabinet::setTemperatureSetpoint(double temperature) { + int16_t rawValue = static_cast(temperature * 100.0); + return setRawTemperatureSetpoint(rawValue); +} + +double MatterTemperatureControlledCabinet::getTemperatureSetpoint() { + if (!useTemperatureNumber) { + log_e("Temperature setpoint methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return 0.0; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::TemperatureSetpoint::Id, &tempVal)) { + rawTempSetpoint = tempVal.val.i16; + } + return (double)rawTempSetpoint / 100.0; +} + +bool MatterTemperatureControlledCabinet::setRawMinTemperature(int16_t _rawTemperature) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (!useTemperatureNumber) { + log_e("Min temperature methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return false; + } + + if (rawMinTemperature == _rawTemperature) { + return true; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (!getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MinTemperature::Id, &tempVal)) { + log_e("Failed to get Temperature Controlled Cabinet Min Temperature Attribute."); + return false; + } + if (tempVal.val.i16 != _rawTemperature) { + tempVal.val.i16 = _rawTemperature; + bool ret; + ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MinTemperature::Id, &tempVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Min Temperature Attribute."); + return false; + } + rawMinTemperature = _rawTemperature; + } + log_v("Temperature Controlled Cabinet min temperature set to %.02fC", (float)_rawTemperature / 100.00); + + return true; +} + +bool MatterTemperatureControlledCabinet::setMinTemperature(double temperature) { + int16_t rawValue = static_cast(temperature * 100.0); + return setRawMinTemperature(rawValue); +} + +double MatterTemperatureControlledCabinet::getMinTemperature() { + if (!useTemperatureNumber) { + log_e("Min temperature methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return 0.0; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MinTemperature::Id, &tempVal)) { + rawMinTemperature = tempVal.val.i16; + } + return (double)rawMinTemperature / 100.0; +} + +bool MatterTemperatureControlledCabinet::setRawMaxTemperature(int16_t _rawTemperature) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (!useTemperatureNumber) { + log_e("Max temperature methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return false; + } + + if (rawMaxTemperature == _rawTemperature) { + return true; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (!getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MaxTemperature::Id, &tempVal)) { + log_e("Failed to get Temperature Controlled Cabinet Max Temperature Attribute."); + return false; + } + if (tempVal.val.i16 != _rawTemperature) { + tempVal.val.i16 = _rawTemperature; + bool ret; + ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MaxTemperature::Id, &tempVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Max Temperature Attribute."); + return false; + } + rawMaxTemperature = _rawTemperature; + } + log_v("Temperature Controlled Cabinet max temperature set to %.02fC", (float)_rawTemperature / 100.00); + + return true; +} + +bool MatterTemperatureControlledCabinet::setMaxTemperature(double temperature) { + int16_t rawValue = static_cast(temperature * 100.0); + return setRawMaxTemperature(rawValue); +} + +double MatterTemperatureControlledCabinet::getMaxTemperature() { + if (!useTemperatureNumber) { + log_e("Max temperature methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return 0.0; + } + + esp_matter_attr_val_t tempVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::MaxTemperature::Id, &tempVal)) { + rawMaxTemperature = tempVal.val.i16; + } + return (double)rawMaxTemperature / 100.0; +} + +bool MatterTemperatureControlledCabinet::setRawStep(int16_t _rawStep) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (!useTemperatureNumber) { + log_e("Temperature step methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return false; + } + + if (rawStep == _rawStep) { + return true; + } + + esp_matter_attr_val_t stepVal = esp_matter_invalid(NULL); + if (!getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::Step::Id, &stepVal)) { + log_e("Failed to get Temperature Controlled Cabinet Step Attribute. Temperature_step feature may not be enabled."); + return false; + } + if (stepVal.val.i16 != _rawStep) { + stepVal.val.i16 = _rawStep; + bool ret; + ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::Step::Id, &stepVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Step Attribute."); + return false; + } + rawStep = _rawStep; + } + log_v("Temperature Controlled Cabinet step set to %.02fC", (float)_rawStep / 100.00); + + return true; +} + +bool MatterTemperatureControlledCabinet::setStep(double step) { + int16_t rawValue = static_cast(step * 100.0); + return setRawStep(rawValue); +} + +double MatterTemperatureControlledCabinet::getStep() { + if (!useTemperatureNumber) { + log_e("Temperature step methods require temperature_number feature. Use begin(tempSetpoint, minTemp, maxTemp, step) instead."); + return 0.0; + } + + // Read from attribute (should always exist after begin() due to workaround) + // If read fails, use stored rawStep value from begin() + esp_matter_attr_val_t stepVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::Step::Id, &stepVal)) { + rawStep = stepVal.val.i16; + } + return (double)rawStep / 100.0; +} + +bool MatterTemperatureControlledCabinet::setSelectedTemperatureLevel(uint8_t level) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (useTemperatureNumber) { + log_e("Temperature level methods require temperature_level feature. Use begin(supportedLevels, levelCount, selectedLevel) instead."); + return false; + } + + // Validate that level is in supported levels array + bool levelFound = false; + for (uint16_t i = 0; i < supportedLevelsCount; i++) { + if (supportedLevelsArray[i] == level) { + levelFound = true; + break; + } + } + if (!levelFound) { + log_e("Temperature level %u is not in the supported levels array", level); + return false; + } + if (selectedTempLevel == level) { + return true; + } + + esp_matter_attr_val_t levelVal = esp_matter_invalid(NULL); + if (!getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::SelectedTemperatureLevel::Id, &levelVal)) { + log_e("Failed to get Temperature Controlled Cabinet Selected Temperature Level Attribute."); + return false; + } + if (levelVal.val.u8 != level) { + levelVal.val.u8 = level; + bool ret; + ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::SelectedTemperatureLevel::Id, &levelVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Selected Temperature Level Attribute."); + return false; + } + selectedTempLevel = level; + } + log_v("Temperature Controlled Cabinet selected temperature level set to %u", level); + + return true; +} + +uint8_t MatterTemperatureControlledCabinet::getSelectedTemperatureLevel() { + if (useTemperatureNumber) { + log_e("Temperature level methods require temperature_level feature. Use begin(supportedLevels, levelCount, selectedLevel) instead."); + return 0; + } + + esp_matter_attr_val_t levelVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::SelectedTemperatureLevel::Id, &levelVal)) { + selectedTempLevel = levelVal.val.u8; + } + return selectedTempLevel; +} + +bool MatterTemperatureControlledCabinet::setSupportedTemperatureLevels(uint8_t *levels, uint16_t count) { + if (!started) { + log_e("Matter Temperature Controlled Cabinet device has not begun."); + return false; + } + + if (useTemperatureNumber) { + log_e("Temperature level methods require temperature_level feature. Use begin(supportedLevels, levelCount, selectedLevel) instead."); + return false; + } + + if (levels == nullptr || count == 0) { + log_e("Invalid levels array or count."); + return false; + } + + // Validate against maximum from ESP-Matter + if (count > temperature_control::k_max_temp_level_count) { + log_e("Level count %u exceeds maximum %u", count, temperature_control::k_max_temp_level_count); + return false; + } + + // Copy the array into internal buffer + memcpy(supportedLevelsArray, levels, count * sizeof(uint8_t)); + supportedLevelsCount = count; + + // Use internal copy for Matter attribute update + // Use esp_matter_array helper function which properly initializes the structure + esp_matter_attr_val_t levelsVal = esp_matter_array(supportedLevelsArray, sizeof(uint8_t), count); + + bool ret = updateAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::SupportedTemperatureLevels::Id, &levelsVal); + if (!ret) { + log_e("Failed to update Temperature Controlled Cabinet Supported Temperature Levels Attribute."); + return false; + } + log_v("Temperature Controlled Cabinet supported temperature levels updated, count: %u", count); + + return true; +} + +uint16_t MatterTemperatureControlledCabinet::getSupportedTemperatureLevelsCount() { + if (useTemperatureNumber) { + log_e("Temperature level methods require temperature_level feature. Use begin(supportedLevels, levelCount, selectedLevel) instead."); + return 0; + } + + esp_matter_attr_val_t levelsVal = esp_matter_invalid(NULL); + if (getAttributeVal(TemperatureControl::Id, TemperatureControl::Attributes::SupportedTemperatureLevels::Id, &levelsVal)) { + return levelsVal.val.a.n; // a.n is the count (number of elements) + } + return 0; +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.h b/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.h new file mode 100644 index 00000000000..09377cf85bb --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterTemperatureControlledCabinet.h @@ -0,0 +1,95 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterTemperatureControlledCabinet : public MatterEndPoint { +public: + MatterTemperatureControlledCabinet(); + ~MatterTemperatureControlledCabinet(); + + // begin with temperature_number feature (mutually exclusive with temperature_level) + // This enables temperature setpoint control with min/max limits and optional step + bool begin(double tempSetpoint = 0.00, double minTemperature = -10.0, double maxTemperature = 32.0, double step = 0.50); + + // begin with temperature_level feature (mutually exclusive with temperature_number) + // This enables temperature level control with an array of supported levels + bool begin(uint8_t *supportedLevels, uint16_t levelCount, uint8_t selectedLevel = 0); + + // this will stop processing Temperature Controlled Cabinet Matter events + void end(); + + // set the temperature setpoint + bool setTemperatureSetpoint(double temperature); + // returns the temperature setpoint in Celsius + double getTemperatureSetpoint(); + + // set the minimum temperature + bool setMinTemperature(double temperature); + // returns the minimum temperature in Celsius + double getMinTemperature(); + + // set the maximum temperature + bool setMaxTemperature(double temperature); + // returns the maximum temperature in Celsius + double getMaxTemperature(); + + // set the temperature step (optional, requires temperature_step feature) + bool setStep(double step); + // returns the temperature step in Celsius + double getStep(); + + // set the selected temperature level (optional, requires temperature_level feature) + bool setSelectedTemperatureLevel(uint8_t level); + // returns the selected temperature level + uint8_t getSelectedTemperatureLevel(); + + // set supported temperature levels (optional, requires temperature_level feature) + bool setSupportedTemperatureLevels(uint8_t *levels, uint16_t count); + // get supported temperature levels count + uint16_t getSupportedTemperatureLevelsCount(); + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + // Feature mode: true = temperature_number, false = temperature_level + // Note: temperature_number and temperature_level are mutually exclusive + bool useTemperatureNumber = true; + + // temperature in 1/100th Celsius (stored as int16_t by multiplying by 100) + int16_t rawTempSetpoint = 0; + int16_t rawMinTemperature = 0; + int16_t rawMaxTemperature = 0; + int16_t rawStep = 0; + uint8_t selectedTempLevel = 0; + // Fixed-size buffer for supported temperature levels (max 16 as per Matter spec: temperature_control::k_max_temp_level_count) + uint8_t supportedLevelsArray[16]; // Size matches esp_matter::cluster::temperature_control::k_max_temp_level_count + uint16_t supportedLevelsCount = 0; + + // internal functions to set the raw temperature values (Matter Cluster) + bool setRawTemperatureSetpoint(int16_t _rawTemperature); + bool setRawMinTemperature(int16_t _rawTemperature); + bool setRawMaxTemperature(int16_t _rawTemperature); + bool setRawStep(int16_t _rawStep); + bool begin(int16_t _rawTempSetpoint, int16_t _rawMinTemperature, int16_t _rawMaxTemperature, int16_t _rawStep); + bool beginInternal(uint8_t *supportedLevels, uint16_t levelCount, uint8_t selectedLevel); +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp b/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp index 6ee18ef0cc9..667929bd6ed 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterThermostat.cpp @@ -302,16 +302,16 @@ bool MatterThermostat::setRawTemperature(int16_t _rawTemperature, uint32_t attri return true; } -bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature) { +bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCoolingTemperature) { // at least one of the setpoints must be valid - bool settingCooling = _setpointCollingTemperature != (float)0xffff; + bool settingCooling = _setpointCoolingTemperature != (float)0xffff; bool settingHeating = _setpointHeatingTemperature != (float)0xffff; if (!settingCooling && !settingHeating) { log_e("Invalid Setpoints values. Set correctly at least one of them in Celsius."); return false; } int16_t _rawHeatValue = static_cast(_setpointHeatingTemperature * 100.0f); - int16_t _rawCoolValue = static_cast(_setpointCollingTemperature * 100.0f); + int16_t _rawCoolValue = static_cast(_setpointCoolingTemperature * 100.0f); // check limits for the setpoints if (settingHeating && (_rawHeatValue < kDefaultMinHeatSetpointLimit || _rawHeatValue > kDefaultMaxHeatSetpointLimit)) { @@ -323,7 +323,7 @@ bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTempera } if (settingCooling && (_rawCoolValue < kDefaultMinCoolSetpointLimit || _rawCoolValue > kDefaultMaxCoolSetpointLimit)) { log_e( - "Invalid Cooling Setpoint value: %.01fC - valid range %d..%d", _setpointCollingTemperature, kDefaultMinCoolSetpointLimit / 100, + "Invalid Cooling Setpoint value: %.01fC - valid range %d..%d", _setpointCoolingTemperature, kDefaultMinCoolSetpointLimit / 100, kDefaultMaxCoolSetpointLimit / 100 ); return false; @@ -337,7 +337,7 @@ bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTempera // only setting Cooling Setpoint if (settingCooling && !settingHeating && _rawCoolValue < (heatingSetpointTemperature + (kDefaultDeadBand * 10))) { log_e( - "AutoMode :: Invalid Cooling Setpoint value: %.01fC - must be higher or equal than %.01fC", _setpointCollingTemperature, getHeatingSetpoint() + deadband + "AutoMode :: Invalid Cooling Setpoint value: %.01fC - must be higher or equal than %.01fC", _setpointCoolingTemperature, getHeatingSetpoint() + deadband ); return false; } @@ -352,7 +352,7 @@ bool MatterThermostat::setCoolingHeatingSetpoints(double _setpointHeatingTempera if (settingCooling && settingHeating && (_rawCoolValue <= _rawHeatValue || _rawCoolValue - _rawHeatValue < kDefaultDeadBand * 10.0)) { log_e( "AutoMode :: Error - Heating Setpoint %.01fC must be lower than Cooling Setpoint %.01fC with a minimum difference of %0.1fC", - _setpointHeatingTemperature, _setpointCollingTemperature, deadband + _setpointHeatingTemperature, _setpointCoolingTemperature, deadband ); return false; } diff --git a/libraries/Matter/src/MatterEndpoints/MatterThermostat.h b/libraries/Matter/src/MatterEndpoints/MatterThermostat.h index 53df61d191e..e4fdefd34ad 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterThermostat.h +++ b/libraries/Matter/src/MatterEndpoints/MatterThermostat.h @@ -101,19 +101,19 @@ class MatterThermostat : public MatterEndPoint { // Heating Setpoint must be lower than Cooling Setpoint // When using AUTO mode the Cooling Setpoint must be higher than Heating Setpoint by at least the 2.5C (deadband) // Thermostat Matter Server will enforce those rules and the Max/Min setpoints limits as in the Matter Specification - bool setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCollingTemperature); + bool setCoolingHeatingSetpoints(double _setpointHeatingTemperature, double _setpointCoolingTemperature); // set the heating setpoint in 1/100th of a Celsio degree bool setHeatingSetpoint(double _setpointHeatingTemperature) { - return setCoolingHeatingSetpoints((double)0xffff, _setpointHeatingTemperature); + return setCoolingHeatingSetpoints(_setpointHeatingTemperature, (double)0xffff); } // get the heating setpoint in 1/100th of a Celsio degree double getHeatingSetpoint() { return heatingSetpointTemperature / 100.0; } // set the cooling setpoint in 1/100th of a Celsio degree - bool setCoolingSetpoint(double _setpointCollingTemperature) { - return setCoolingHeatingSetpoints(_setpointCollingTemperature, (double)0xffff); + bool setCoolingSetpoint(double _setpointCoolingTemperature) { + return setCoolingHeatingSetpoints((double)0xffff, _setpointCoolingTemperature); } // get the cooling setpoint in 1/100th of a Celsio degree double getCoolingSetpoint() { diff --git a/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp b/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp new file mode 100644 index 00000000000..1b4b4d7bffa --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.cpp @@ -0,0 +1,104 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +bool MatterWaterFreezeDetector::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter Water Freeze Detector device has not begun."); + return false; + } + + log_d("Water Freeze Detector Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + return ret; +} + +MatterWaterFreezeDetector::MatterWaterFreezeDetector() {} + +MatterWaterFreezeDetector::~MatterWaterFreezeDetector() { + end(); +} + +bool MatterWaterFreezeDetector::begin(bool _freezeState) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Matter Water Freeze Detector with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + water_freeze_detector::config_t water_freeze_detector_config; + water_freeze_detector_config.boolean_state.state_value = _freezeState; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = water_freeze_detector::create(node::get(), &water_freeze_detector_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Water Freeze Detector endpoint"); + return false; + } + freezeState = _freezeState; + setEndPointId(endpoint::get_id(endpoint)); + log_i("Water Freeze Detector created with endpoint_id %d", getEndPointId()); + + started = true; + return true; +} + +void MatterWaterFreezeDetector::end() { + started = false; +} + +bool MatterWaterFreezeDetector::setFreeze(bool _freezeState) { + if (!started) { + log_e("Matter Water Freeze Detector device has not begun."); + return false; + } + + // avoid processing if there was no change + if (freezeState == _freezeState) { + return true; + } + + esp_matter_attr_val_t freezeVal = esp_matter_invalid(NULL); + + if (!getAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &freezeVal)) { + log_e("Failed to get Water Freeze Detector Attribute."); + return false; + } + if (freezeVal.val.u8 != _freezeState) { + freezeVal.val.u8 = _freezeState; + bool ret; + ret = updateAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &freezeVal); + if (!ret) { + log_e("Failed to update Water Freeze Detector Attribute."); + return false; + } + freezeState = _freezeState; + } + log_v("Water Freeze Detector set to %s", _freezeState ? "Detected" : "Not Detected"); + + return true; +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.h b/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.h new file mode 100644 index 00000000000..350aedfe775 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWaterFreezeDetector.h @@ -0,0 +1,54 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterWaterFreezeDetector : public MatterEndPoint { +public: + MatterWaterFreezeDetector(); + ~MatterWaterFreezeDetector(); + // begin Matter Water Freeze Detector endpoint with initial freeze state + bool begin(bool _freezeState = false); + // this will just stop processing Water Freeze Detector Matter events + void end(); + + // set the freeze state + bool setFreeze(bool _freezeState); + // returns the freeze state + bool getFreeze() { + return freezeState; + } + + // bool conversion operator + void operator=(bool _freezeState) { + setFreeze(_freezeState); + } + // bool conversion operator + operator bool() { + return getFreeze(); + } + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + bool freezeState = false; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp b/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp new file mode 100644 index 00000000000..e079807286f --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.cpp @@ -0,0 +1,104 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace chip::app::Clusters; + +bool MatterWaterLeakDetector::attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val) { + bool ret = true; + if (!started) { + log_e("Matter Water Leak Detector device has not begun."); + return false; + } + + log_d("Water Leak Detector Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); + return ret; +} + +MatterWaterLeakDetector::MatterWaterLeakDetector() {} + +MatterWaterLeakDetector::~MatterWaterLeakDetector() { + end(); +} + +bool MatterWaterLeakDetector::begin(bool _leakState) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Matter Water Leak Detector with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + water_leak_detector::config_t water_leak_detector_config; + water_leak_detector_config.boolean_state.state_value = _leakState; + + // endpoint handles can be used to add/modify clusters. + endpoint_t *endpoint = water_leak_detector::create(node::get(), &water_leak_detector_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create Water Leak Detector endpoint"); + return false; + } + leakState = _leakState; + setEndPointId(endpoint::get_id(endpoint)); + log_i("Water Leak Detector created with endpoint_id %d", getEndPointId()); + + started = true; + return true; +} + +void MatterWaterLeakDetector::end() { + started = false; +} + +bool MatterWaterLeakDetector::setLeak(bool _leakState) { + if (!started) { + log_e("Matter Water Leak Detector device has not begun."); + return false; + } + + // avoid processing if there was no change + if (leakState == _leakState) { + return true; + } + + esp_matter_attr_val_t leakVal = esp_matter_invalid(NULL); + + if (!getAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &leakVal)) { + log_e("Failed to get Water Leak Detector Attribute."); + return false; + } + if (leakVal.val.u8 != _leakState) { + leakVal.val.u8 = _leakState; + bool ret; + ret = updateAttributeVal(BooleanState::Id, BooleanState::Attributes::StateValue::Id, &leakVal); + if (!ret) { + log_e("Failed to update Water Leak Detector Attribute."); + return false; + } + leakState = _leakState; + } + log_v("Water Leak Detector set to %s", _leakState ? "Detected" : "Not Detected"); + + return true; +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.h b/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.h new file mode 100644 index 00000000000..e94dd3eb525 --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWaterLeakDetector.h @@ -0,0 +1,54 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include + +class MatterWaterLeakDetector : public MatterEndPoint { +public: + MatterWaterLeakDetector(); + ~MatterWaterLeakDetector(); + // begin Matter Water Leak Detector endpoint with initial leak state + bool begin(bool _leakState = false); + // this will just stop processing Water Leak Detector Matter events + void end(); + + // set the leak state + bool setLeak(bool _leakState); + // returns the leak state + bool getLeak() { + return leakState; + } + + // bool conversion operator + void operator=(bool _leakState) { + setLeak(_leakState); + } + // bool conversion operator + operator bool() { + return getLeak(); + } + + // this function is called by Matter internal event processor. It could be overwritten by the application, if necessary. + bool attributeChangeCB(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, esp_matter_attr_val_t *val); + +protected: + bool started = false; + bool leakState = false; +}; +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/NetBIOS/examples/ESP_NBNST/ci.json b/libraries/NetBIOS/examples/ESP_NBNST/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetBIOS/examples/ESP_NBNST/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetBIOS/examples/ESP_NBNST/ci.yml b/libraries/NetBIOS/examples/ESP_NBNST/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetBIOS/examples/ESP_NBNST/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetBIOS/library.properties b/libraries/NetBIOS/library.properties index 71d22d6f363..527a4aa1835 100644 --- a/libraries/NetBIOS/library.properties +++ b/libraries/NetBIOS/library.properties @@ -1,5 +1,5 @@ name=NetBIOS -version=3.3.0 +version=3.3.4 author=Pablo@xpablo.cz maintainer=Hristo Gochkov sentence=Enables NBNS (NetBIOS) name resolution. diff --git a/libraries/Network/library.properties b/libraries/Network/library.properties index f7e04f4de3b..f6c465eebb5 100644 --- a/libraries/Network/library.properties +++ b/libraries/Network/library.properties @@ -1,5 +1,5 @@ name=Networking -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=General network management library. diff --git a/libraries/Network/src/NetworkEvents.cpp b/libraries/Network/src/NetworkEvents.cpp index 161e55c5d01..195499c5138 100644 --- a/libraries/Network/src/NetworkEvents.cpp +++ b/libraries/Network/src/NetworkEvents.cpp @@ -3,6 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ + +#include //std::nothrow + #include "NetworkEvents.h" #include "NetworkManager.h" #include "esp_task.h" @@ -82,7 +85,7 @@ bool NetworkEvents::postEvent(const arduino_event_t *data) { if (data == NULL || _arduino_event_queue == NULL) { return false; } - arduino_event_t *event = new arduino_event_t(); + arduino_event_t *event = new (std::nothrow) arduino_event_t(); if (event == NULL) { log_e("Arduino Event Malloc Failed!"); return false; diff --git a/libraries/Network/src/NetworkEvents.h b/libraries/Network/src/NetworkEvents.h index 34a54cab092..e82d6a5dfb4 100644 --- a/libraries/Network/src/NetworkEvents.h +++ b/libraries/Network/src/NetworkEvents.h @@ -99,9 +99,13 @@ typedef enum { typedef union { ip_event_ap_staipassigned_t wifi_ap_staipassigned; ip_event_got_ip_t got_ip; + ip_event_got_ip_t lost_ip; ip_event_got_ip6_t got_ip6; #if CONFIG_ETH_ENABLED + esp_eth_handle_t eth_started; + esp_eth_handle_t eth_stopped; esp_eth_handle_t eth_connected; + esp_eth_handle_t eth_disconnected; #endif #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED wifi_event_sta_scan_done_t wifi_scan_done; diff --git a/libraries/Network/src/NetworkInterface.cpp b/libraries/Network/src/NetworkInterface.cpp index 06cf2a377b0..393d2b9cf7f 100644 --- a/libraries/Network/src/NetworkInterface.cpp +++ b/libraries/Network/src/NetworkInterface.cpp @@ -47,6 +47,7 @@ extern "C" int lwip_hook_ip6_input(struct pbuf *p, struct netif *inp) { #endif static void _ip_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + (void)arg; if (event_base == IP_EVENT) { NetworkInterface *netif = NULL; if (event_id == IP_EVENT_STA_GOT_IP || event_id == IP_EVENT_ETH_GOT_IP || event_id == IP_EVENT_PPP_GOT_IP) { @@ -96,6 +97,7 @@ void NetworkInterface::_onIpEvent(int32_t event_id, void *event_data) { #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE log_v("%s Lost IP", desc()); #endif + memcpy(&arduino_event.event_info.lost_ip, event_data, sizeof(ip_event_got_ip_t)); #if SOC_WIFI_SUPPORTED || CONFIG_ESP_WIFI_REMOTE_ENABLED if (_interface_id == ESP_NETIF_ID_STA) { arduino_event.event_id = ARDUINO_EVENT_WIFI_STA_LOST_IP; @@ -350,7 +352,7 @@ bool NetworkInterface::enableIPv6(bool en) { } bool NetworkInterface::dnsIP(uint8_t dns_no, IPAddress ip) { - if (_esp_netif == NULL || dns_no > 2) { + if (_esp_netif == NULL || dns_no >= ESP_NETIF_DNS_MAX) { return false; } esp_netif_flags_t flags = esp_netif_get_flags(_esp_netif); @@ -706,11 +708,11 @@ IPAddress NetworkInterface::gatewayIP() const { } IPAddress NetworkInterface::dnsIP(uint8_t dns_no) const { - if (_esp_netif == NULL) { + if (_esp_netif == NULL || dns_no >= ESP_NETIF_DNS_MAX) { return IPAddress(); } esp_netif_dns_info_t d; - if (esp_netif_get_dns_info(_esp_netif, dns_no ? ESP_NETIF_DNS_BACKUP : ESP_NETIF_DNS_MAIN, &d) != ESP_OK) { + if (esp_netif_get_dns_info(_esp_netif, (esp_netif_dns_type_t)dns_no, &d) != ESP_OK) { return IPAddress(); } if (d.ip.type == ESP_IPADDR_TYPE_V6) { diff --git a/libraries/Network/src/NetworkManager.cpp b/libraries/Network/src/NetworkManager.cpp index 12276b2e242..a19cf52dd87 100644 --- a/libraries/Network/src/NetworkManager.cpp +++ b/libraries/Network/src/NetworkManager.cpp @@ -185,6 +185,18 @@ NetworkInterface *NetworkManager::getDefaultInterface() { return NULL; } +bool NetworkManager::isOnline() { + for (int i = 0; i < ESP_NETIF_ID_MAX; ++i) { + if (i != ESP_NETIF_ID_AP) { + NetworkInterface *iface = getNetifByID((Network_Interface_ID)i); + if (iface != NULL && iface->connected() && (iface->hasIP() || iface->hasGlobalIPv6())) { + return true; + } + } + } + return false; +} + size_t NetworkManager::printTo(Print &out) const { size_t bytes = 0; diff --git a/libraries/Network/src/NetworkManager.h b/libraries/Network/src/NetworkManager.h index 6b9d5e16cfc..063de721792 100644 --- a/libraries/Network/src/NetworkManager.h +++ b/libraries/Network/src/NetworkManager.h @@ -22,6 +22,9 @@ class NetworkManager : public NetworkEvents, public Printable { bool setDefaultInterface(NetworkInterface &ifc); NetworkInterface *getDefaultInterface(); + // Returns true if any interface (except AP) has assigned IPv4 or global IPv6 + bool isOnline(); + size_t printTo(Print &out) const; static const char *getHostname(); diff --git a/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientInsecure/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientPSK/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.json deleted file mode 100644 index cbdd28f773d..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.yml new file mode 100644 index 00000000000..9f15b3468e6 --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientSecure/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.json deleted file mode 100644 index 04eb62b977a..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.yml new file mode 100644 index 00000000000..e412162e577 --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.json b/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.yml b/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/NetworkClientSecure/library.properties b/libraries/NetworkClientSecure/library.properties index 6ebf4c0ec70..2b867985222 100644 --- a/libraries/NetworkClientSecure/library.properties +++ b/libraries/NetworkClientSecure/library.properties @@ -1,5 +1,5 @@ name=NetworkClientSecure -version=3.3.0 +version=3.3.4 author=Evandro Luis Copercini maintainer=Github Community sentence=Enables secure network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/NetworkClientSecure/src/NetworkClientSecure.cpp b/libraries/NetworkClientSecure/src/NetworkClientSecure.cpp index b24c9f1adc3..08da928668e 100644 --- a/libraries/NetworkClientSecure/src/NetworkClientSecure.cpp +++ b/libraries/NetworkClientSecure/src/NetworkClientSecure.cpp @@ -227,6 +227,10 @@ size_t NetworkClientSecure::write(const uint8_t *buf, size_t size) { return 0; } + if (size == 0) { + return 0; + } + if (_stillinPlainStart) { return send_net_data(sslclient.get(), buf, size); } diff --git a/libraries/NetworkClientSecure/src/ssl_client.cpp b/libraries/NetworkClientSecure/src/ssl_client.cpp index 19f75673133..71cfa93b5c0 100644 --- a/libraries/NetworkClientSecure/src/ssl_client.cpp +++ b/libraries/NetworkClientSecure/src/ssl_client.cpp @@ -223,9 +223,10 @@ int start_ssl_client( log_e("pre-shared key not valid hex or too long"); return -1; } - unsigned char psk[MBEDTLS_PSK_MAX_LEN]; - size_t psk_len = strlen(psKey) / 2; - for (int j = 0; j < strlen(psKey); j += 2) { + unsigned char pskBytes[MBEDTLS_PSK_MAX_LEN]; + size_t pskStrLen = strlen(psKey); + size_t pskByteLen = pskStrLen / 2; + for (int j = 0; j < pskStrLen; j += 2) { char c = psKey[j]; if (c >= '0' && c <= '9') { c -= '0'; @@ -236,7 +237,7 @@ int start_ssl_client( } else { return -1; } - psk[j / 2] = c << 4; + pskBytes[j / 2] = c << 4; c = psKey[j + 1]; if (c >= '0' && c <= '9') { c -= '0'; @@ -247,10 +248,10 @@ int start_ssl_client( } else { return -1; } - psk[j / 2] |= c; + pskBytes[j / 2] |= c; } // set mbedtls config - ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, (const unsigned char *)pskIdent, strlen(pskIdent)); + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, pskBytes, pskByteLen, (const unsigned char *)pskIdent, strlen(pskIdent)); if (ret != 0) { log_e("mbedtls_ssl_conf_psk returned %d", ret); return handle_error(ret); @@ -409,25 +410,41 @@ int data_to_read(sslclient_context *ssl_client) { } int send_ssl_data(sslclient_context *ssl_client, const uint8_t *data, size_t len) { - unsigned long write_start_time = millis(); - int ret = -1; + if (len == 0) { + return 0; // Skipping zero-length write + } + + static constexpr size_t max_write_chunk_size = 4096; + unsigned long last_progress = millis(); // Timeout since last progress + size_t sent = 0; + + while (sent < len) { + size_t to_send = len - sent; + if (to_send > max_write_chunk_size) { + to_send = max_write_chunk_size; + } - while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) { - if ((millis() - write_start_time) > ssl_client->socket_timeout) { + int ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data + sent, to_send); + if (ret > 0) { + sent += ret; + last_progress = millis(); // refresh timeout window + continue; + } + + if ((millis() - last_progress) > ssl_client->socket_timeout) { log_v("SSL write timed out."); return -1; } if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { - log_v("Handling error %d", ret); //for low level debug + log_v("Handling error %d", ret); return handle_error(ret); } - //wait for space to become available vTaskDelay(2); } - return ret; + return (int)sent; } // Some protocols, such as SMTP, XMPP, MySQL/Posgress and various others diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_lamp/README.md b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/README.md new file mode 100644 index 00000000000..2efd04d30cd --- /dev/null +++ b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/README.md @@ -0,0 +1,165 @@ +# OpenThread CoAP Lamp Example + +This example demonstrates how to create a CoAP (Constrained Application Protocol) server on a Thread network that controls an RGB LED lamp.\ +The application acts as a CoAP resource server that receives PUT requests to turn the lamp on or off, demonstrating Thread-based IoT device communication. + +## Supported Targets + +| SoC | Thread | RGB LED | Status | +| --- | ------ | ------- | ------ | +| ESP32-H2 | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | Required | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example requires a companion CoAP Switch device (coap_switch example) to control the lamp. +- The lamp device acts as a Leader node and CoAP server. + +## Features + +- CoAP server implementation on Thread network +- RGB LED control with smooth fade in/out transitions +- Leader node configuration using CLI Helper Functions API +- CoAP resource creation and management +- Multicast IPv6 address for CoAP communication +- Automatic network setup with retry mechanism +- Visual status indication using RGB LED (Red = failed, Green = ready) + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- RGB LED (built-in RGB LED or external RGB LED) +- USB cable for Serial communication +- A CoAP Switch device (coap_switch example) to control the lamp + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, you can modify the network and CoAP configuration: + +```cpp +#define OT_CHANNEL "24" +#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff" +#define OT_MCAST_ADDR "ff05::abcd" +#define OT_COAP_RESOURCE_NAME "Lamp" +``` + +**Important:** +- The network key and channel must match the Switch device configuration +- The multicast address and resource name must match the Switch device +- The network key must be a 32-character hexadecimal string (16 bytes) +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) + +## Building and Flashing + +1. Open the `coap_lamp.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +Starting OpenThread. +Running as Lamp (RGB LED) - use the other C6/H2 as a Switch +OpenThread started. +Waiting for activating correct Device Role. +........ +Device is Leader. +OpenThread setup done. Node is ready. +``` + +The RGB LED will turn **green** when the device is ready to receive CoAP commands. + +## Using the Device + +### Lamp Device Setup + +The lamp device automatically: +1. Configures itself as a Thread Leader node +2. Creates a CoAP server +3. Registers a CoAP resource named "Lamp" +4. Sets up a multicast IPv6 address for CoAP communication +5. Waits for CoAP PUT requests from the Switch device + +### CoAP Resource + +The lamp exposes a CoAP resource that accepts: +- **PUT with payload "0"**: Turns the lamp OFF (fades to black) +- **PUT with payload "1"**: Turns the lamp ON (fades to white) + +### Visual Status Indication + +The RGB LED provides visual feedback: +- **Red**: Setup failed or error occurred +- **Green**: Device is ready and waiting for CoAP commands +- **White/Black**: Lamp state (controlled by CoAP commands) + +### Working with Switch Device + +1. Start the Lamp device first (this example) +2. Start the Switch device (coap_switch example) with matching network key and channel +3. Press the button on the Switch device to toggle the lamp +4. The lamp will fade in/out smoothly when toggled + +## Code Structure + +The coap_lamp example consists of the following main components: + +1. **`otDeviceSetup()` function**: + - Configures the device as a Leader node using CLI Helper Functions + - Sets up CoAP server and resource + - Waits for device to become Leader + - Returns success/failure status + +2. **`setupNode()` function**: + - Retries setup until successful + - Calls `otDeviceSetup()` with Leader role configuration + +3. **`otCOAPListen()` function**: + - Listens for CoAP requests from the Switch device + - Parses CoAP PUT requests + - Controls RGB LED based on payload (0 = OFF, 1 = ON) + - Implements smooth fade transitions + +4. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` + - Initializes OpenThread CLI + - Sets CLI timeout + - Calls `setupNode()` to configure the device + +5. **`loop()`**: + - Continuously calls `otCOAPListen()` to process incoming CoAP requests + - Small delay for responsiveness + +## Troubleshooting + +- **LED stays red**: Setup failed. Check Serial Monitor for error messages. Verify network configuration. +- **Lamp not responding to switch**: Ensure both devices use the same network key, channel, multicast address, and resource name. Check that Switch device is running. +- **Device not becoming Leader**: Clear NVS or ensure this is the first device started. Check network configuration. +- **CoAP requests not received**: Verify multicast address matches between Lamp and Switch devices. Check Thread network connectivity. +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) +- [CoAP Protocol](https://coap.technology/) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.json b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.yml b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/COAP/coap_lamp/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_switch/README.md b/libraries/OpenThread/examples/CLI/COAP/coap_switch/README.md new file mode 100644 index 00000000000..9f8aae20539 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/COAP/coap_switch/README.md @@ -0,0 +1,176 @@ +# OpenThread CoAP Switch Example + +This example demonstrates how to create a CoAP (Constrained Application Protocol) client on a Thread network that controls a remote CoAP server (lamp).\ +The application acts as a CoAP client that sends PUT requests to a lamp device, demonstrating Thread-based IoT device control. + +## Supported Targets + +| SoC | Thread | Button | Status | +| --- | ------ | ------ | ------ | +| ESP32-H2 | ✅ | Required | Fully supported | +| ESP32-C6 | ✅ | Required | Fully supported | +| ESP32-C5 | ✅ | Required | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example requires a companion CoAP Lamp device (coap_lamp example) to control. +- The switch device joins the network as a Router or Child node and acts as a CoAP client. + +## Features + +- CoAP client implementation on Thread network +- Button-based control of remote lamp device +- Router/Child node configuration using CLI Helper Functions API +- CoAP PUT request sending with confirmation +- Automatic network join with retry mechanism +- Visual status indication using RGB LED (Red = failed, Blue = ready) +- Button debouncing for reliable input + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- User button (BOOT button or external button) +- RGB LED for status indication (optional, but recommended) +- USB cable for Serial communication +- A CoAP Lamp device (coap_lamp example) must be running first + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, you can modify the network and CoAP configuration: + +```cpp +#define USER_BUTTON 9 // C6/H2 Boot button (change if needed) +#define OT_CHANNEL "24" +#define OT_NETWORK_KEY "00112233445566778899aabbccddeeff" +#define OT_MCAST_ADDR "ff05::abcd" +#define OT_COAP_RESOURCE_NAME "Lamp" +``` + +**Important:** +- The network key and channel **must match** the Lamp device configuration +- The multicast address and resource name **must match** the Lamp device +- The network key must be a 32-character hexadecimal string (16 bytes) +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) +- **Start the Lamp device first** before starting this Switch device + +## Building and Flashing + +1. **First, start the Lamp device** using the coap_lamp example +2. Open the `coap_switch.ino` sketch in the Arduino IDE. +3. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +4. Connect your ESP32 board to your computer via USB. +5. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +Starting OpenThread. +Running as Switch - use the BOOT button to toggle the other C6/H2 as a Lamp +OpenThread started. +Waiting for activating correct Device Role. +........ +Device is Router. +OpenThread setup done. Node is ready. +``` + +The RGB LED will turn **blue** when the device is ready to send CoAP commands. + +## Using the Device + +### Switch Device Setup + +The switch device automatically: +1. Joins the existing Thread network (created by the Lamp Leader) +2. Configures itself as a Router or Child node +3. Creates a CoAP client +4. Waits for button presses to send CoAP commands + +### Button Control + +- **Press the button**: Toggles the lamp state (ON/OFF) +- The switch sends CoAP PUT requests to the lamp: + - Payload "1" = Turn lamp ON + - Payload "0" = Turn lamp OFF + +### Visual Status Indication + +The RGB LED provides visual feedback: +- **Red**: Setup failed or CoAP request failed +- **Blue**: Device is ready and can send CoAP commands +- **Red (after button press)**: CoAP request failed, device will restart setup + +### Working with Lamp Device + +1. Start the Lamp device first (coap_lamp example) +2. Start this Switch device with matching network key and channel +3. Wait for Switch device to join the network (LED turns blue) +4. Press the button on the Switch device +5. The lamp on the other device should toggle ON/OFF + +## Code Structure + +The coap_switch example consists of the following main components: + +1. **`otDeviceSetup()` function**: + - Configures the device to join an existing network using CLI Helper Functions + - Sets up CoAP client + - Waits for device to become Router or Child + - Returns success/failure status + +2. **`setupNode()` function**: + - Retries setup until successful + - Calls `otDeviceSetup()` with Router/Child role configuration + +3. **`otCoapPUT()` function**: + - Sends CoAP PUT request to the lamp device + - Waits for CoAP confirmation response + - Returns success/failure status + - Uses CLI Helper Functions to send commands and read responses + +4. **`checkUserButton()` function**: + - Monitors button state with debouncing + - Toggles lamp state on button press + - Calls `otCoapPUT()` to send commands + - Restarts setup if CoAP request fails + +5. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` + - Initializes OpenThread CLI + - Sets CLI timeout + - Calls `setupNode()` to configure the device + +6. **`loop()`**: + - Continuously calls `checkUserButton()` to monitor button input + - Small delay for responsiveness + +## Troubleshooting + +- **LED stays red**: Setup failed. Check Serial Monitor for error messages. Verify network configuration matches Lamp device. +- **Button press doesn't toggle lamp**: Ensure Lamp device is running and both devices are on the same Thread network. Check that network key, channel, multicast address, and resource name match. +- **Device not joining network**: Ensure Lamp device (Leader) is running first. Verify network key and channel match exactly. +- **CoAP request timeout**: Check Thread network connectivity. Verify multicast address and resource name match the Lamp device. Ensure Lamp device is responding. +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) +- [CoAP Protocol](https://coap.technology/) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.json b/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.yml b/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/COAP/coap_switch/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/SimpleCLI/README.md b/libraries/OpenThread/examples/CLI/SimpleCLI/README.md new file mode 100644 index 00000000000..463d7ed5ab2 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleCLI/README.md @@ -0,0 +1,166 @@ +# OpenThread Simple CLI Example + +This example demonstrates how to use the OpenThread CLI (Command-Line Interface) for interactive control of a Thread network node using an ESP32 SoC microcontroller.\ +The application provides an interactive console where you can manually configure and control the Thread network using OpenThread CLI commands. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin(false)` which does not automatically start a Thread network, allowing you to manually configure it using CLI commands. + +## Features + +- Interactive OpenThread CLI console via Serial Monitor +- Manual Thread network configuration using CLI commands +- Full control over Thread network parameters (network name, channel, PAN ID, network key, etc.) +- Support for all OpenThread CLI commands +- Useful for learning OpenThread CLI and debugging Thread networks + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +No configuration is required before uploading the sketch. The example starts with a fresh Thread stack that is not automatically started, allowing you to configure it manually using CLI commands. + +## Building and Flashing + +1. Open the `SimpleCLI.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +OpenThread CLI started - type 'help' for a list of commands. +ot> +``` + +The `ot> ` prompt indicates that the OpenThread CLI console is ready. You can now type OpenThread CLI commands directly. + +## Using the Device + +### Interactive CLI Commands + +Type OpenThread CLI commands in the Serial Monitor. Some useful commands to get started: + +**Get help:** +``` +help +``` + +**Initialize a new dataset:** +``` +dataset init new +``` + +**Set network parameters:** +``` +dataset networkname MyThreadNetwork +dataset channel 15 +dataset networkkey 00112233445566778899aabbccddeeff +``` + +**Commit and start the network:** +``` +dataset commit active +ifconfig up +thread start +``` + +**Check device state:** +``` +state +``` + +**View network information:** +``` +networkname +channel +panid +ipaddr +``` + +**For a complete list of OpenThread CLI commands, refer to the** [OpenThread CLI Reference](https://openthread.io/reference/cli). + +### Example Workflow + +1. **Initialize a new Thread network (Leader):** + ``` + dataset init new + dataset networkname MyNetwork + dataset channel 15 + dataset networkkey 00112233445566778899aabbccddeeff + dataset commit active + ifconfig up + thread start + ``` + +2. **Join an existing network (Router/Child):** + ``` + dataset clear + dataset networkkey 00112233445566778899aabbccddeeff + dataset channel 15 + dataset commit active + ifconfig up + thread start + ``` + +3. **Check network status:** + ``` + state + networkname + ipaddr + ``` + +## Code Structure + +The SimpleCLI example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` (no auto-start) + - Initializes OpenThread CLI + - Starts the interactive CLI console on Serial + +2. **`loop()`**: + - Empty - all interaction happens through the CLI console + +## Troubleshooting + +- **CLI not responding**: Ensure Serial Monitor is set to 115200 baud and "Both NL & CR" line ending +- **Commands not working**: Make sure OpenThread stack is initialized (check for "OpenThread CLI started" message) +- **Network not starting**: Verify that you've committed the dataset and started the interface before starting Thread +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/SimpleCLI/ci.json b/libraries/OpenThread/examples/CLI/SimpleCLI/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/SimpleCLI/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/SimpleCLI/ci.yml b/libraries/OpenThread/examples/CLI/SimpleCLI/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleCLI/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/SimpleNode/README.md b/libraries/OpenThread/examples/CLI/SimpleNode/README.md new file mode 100644 index 00000000000..c882be09046 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleNode/README.md @@ -0,0 +1,131 @@ +# OpenThread Simple Node Example + +This example demonstrates how to create a simple OpenThread node that automatically starts in a Thread network using default settings.\ +The application automatically initializes a Thread network with default parameters and displays the current device role and network information. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin()` which automatically starts a Thread network using default settings or previously stored NVS dataset information. + +## Features + +- Automatic Thread network initialization with default settings +- Automatic device role assignment (first device becomes Leader, subsequent devices become Router or Child) +- Network information display using CLI Helper Functions API +- Periodic device role monitoring +- Support for persistent network configuration via NVS + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +No configuration is required before uploading the sketch. The example uses default Thread network parameters: + +- **Network Name**: "OpenThread-ESP" +- **Mesh Local Prefix**: "fd00:db8:a0:0::/64" +- **Channel**: 15 +- **PAN ID**: 0x1234 +- **Extended PAN ID**: "dead00beef00cafe" +- **Network Key**: "00112233445566778899aabbccddeeff" +- **PSKc**: "104810e2315100afd6bc9215a6bfac53" + +**Note:** If NVS (Non-Volatile Storage) already contains dataset information, it will be loaded from there instead of using defaults. + +## Building and Flashing + +1. Open the `SimpleNode.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +OpenThread Network Information: +Role: Leader +RLOC16: 0x0000 +Network Name: OpenThread-ESP +Channel: 15 +PAN ID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +Mesh Local EID: fd00:db8:a0:0:0:ff:fe00:0 +Leader RLOC: fd00:db8:a0:0:0:ff:fe00:0 +Node RLOC: fd00:db8:a0:0:0:ff:fe00:0 + +Thread Node State: Leader +Thread Node State: Leader +... +``` + +The first device to start will become the **Leader**. Subsequent devices will automatically join as **Router** or **Child** nodes. + +## Using the Device + +### Network Behavior + +- **First device**: Automatically becomes the Leader and creates a new Thread network +- **Subsequent devices**: Automatically join the existing network as Router or Child nodes +- **Device role**: Displayed every 5 seconds in the Serial Monitor + +### Multiple Devices + +To create a multi-device Thread network: + +1. Flash the first device - it will become the Leader +2. Flash additional devices - they will automatically join the network +3. All devices must use the same network key and channel (defaults are used if NVS is empty) + +## Code Structure + +The SimpleNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin()` (auto-start with default settings) + - Initializes OpenThread CLI + - Prints current Thread network information using `OpenThread.otPrintNetworkInformation()` + +2. **`loop()`**: + - Periodically displays the current device role using `OpenThread.otGetStringDeviceRole()` + - Updates every 5 seconds + +## Troubleshooting + +- **Device not joining network**: Ensure all devices use the same network key and channel. Check that the Leader node is running first. +- **Role stuck as "Detached"**: Wait a few seconds for the device to join the network. Verify network key and channel match the Leader. +- **No network information displayed**: Check that OpenThread stack initialized successfully (look for network information in setup) +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/SimpleNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/SimpleNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/SimpleNode/ci.yml b/libraries/OpenThread/examples/CLI/SimpleNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino index 40f046aeab5..0fb66a5dfb3 100644 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ExtendedRouterNode.ino @@ -16,8 +16,8 @@ #include "OThreadCLI_Util.h" // Leader node shall use the same Network Key and channel -#define CLI_NETWORK_KEY "00112233445566778899aabbccddeeff" -#define CLI_NETWORK_CHANEL "24" +#define CLI_NETWORK_KEY "00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "24" bool otStatus = true; void setup() { @@ -29,7 +29,7 @@ void setup() { otStatus &= otExecCommand("dataset", "clear"); otStatus &= otExecCommand("dataset networkkey", CLI_NETWORK_KEY); - otStatus &= otExecCommand("dataset channel", CLI_NETWORK_CHANEL); + otStatus &= otExecCommand("dataset channel", CLI_NETWORK_CHANNEL); otStatus &= otExecCommand("dataset", "commit active"); otStatus &= otExecCommand("ifconfig", "up"); otStatus &= otExecCommand("thread", "start"); diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/README.md b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/README.md new file mode 100644 index 00000000000..3091b65259a --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/README.md @@ -0,0 +1,163 @@ +# OpenThread Extended Router Node Example (CLI) + +This example demonstrates how to create an OpenThread Router or Child node that joins an existing Thread network, with extended functionality showing both CLI Helper Functions API and native OpenThread API usage.\ +The example shows how to retrieve network information using both methods and demonstrates error handling. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin(false)` which does not automatically start a Thread network, allowing manual configuration. +- **Important:** A Leader node must be running before starting this Router/Child node. + +## Features + +- Manual Router/Child node configuration using CLI Helper Functions API +- Joins an existing Thread network created by a Leader node +- Demonstrates both CLI Helper Functions API (`otGetRespCmd()`) and native OpenThread API (`otLinkGetPanId()`) usage +- Network information display using both API methods +- Error handling and timeout management +- Comprehensive network status monitoring + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication +- A Leader node must be running on the same network + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, configure the network parameters to match the Leader node: + +```cpp +#define CLI_NETWORK_KEY "00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "24" +``` + +**Important:** +- The network key **must match** the Leader node's network key +- The channel **must match** the Leader node's channel +- The network key must be a 32-character hexadecimal string (16 bytes) +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) + +## Building and Flashing + +1. **First, start the Leader node** using the LeaderNode example +2. Open the `ExtendedRouterNode.ino` sketch in the Arduino IDE. +3. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +4. Connect your ESP32 board to your computer via USB. +5. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +Setting up OpenThread Node as Router/Child +Make sure the Leader Node is already running + +PanID[using CLI]: 0x1234 + +PanID[using OT API]: 0x1234 + +Thread NetworkInformation: +--------------------------- +Role: Router +RLOC16: 0xfc00 +Network Name: OpenThread-ESP +Channel: 24 +PAN ID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +Mesh Local EID: fd00:db8:a0:0:0:ff:fe00:fc00 +Leader RLOC: fd00:db8:a0:0:0:ff:fe00:0 +Node RLOC: fd00:db8:a0:0:0:ff:fe00:fc00 +--------------------------- +``` + +## Using the Device + +### Extended Router/Child Node Setup + +The Extended Router/Child node is automatically configured in `setup()` using the following sequence: + +1. **Clear dataset**: Clears any existing dataset +2. **Set network key**: Configures the network security key (must match Leader) +3. **Set channel**: Configures the IEEE 802.15.4 channel (must match Leader) +4. **Commit dataset**: Applies the dataset to the active configuration +5. **Start interface**: Brings up the network interface +6. **Start Thread**: Starts the Thread network and joins the existing network +7. **Wait for role**: Waits up to 90 seconds for the device to become Router or Child + +### Dual API Demonstration + +This example demonstrates two ways to access OpenThread information: + +1. **CLI Helper Functions API**: Uses `otGetRespCmd("panid", resp)` to get PAN ID via CLI +2. **Native OpenThread API**: Uses `otLinkGetPanId(esp_openthread_get_instance())` to get PAN ID directly + +Both methods should return the same value, demonstrating API equivalence. + +### Network Information + +Once the device joins the network, the `loop()` function displays comprehensive network information using `OpenThread.otPrintNetworkInformation()`, including: +- Device role +- RLOC16 +- Network name +- Channel +- PAN ID and Extended PAN ID +- Network key +- IPv6 addresses (Mesh Local EID, Leader RLOC, Node RLOC) + +## Code Structure + +The ExtendedRouterNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` (no auto-start) + - Initializes OpenThread CLI + - Configures the device to join an existing network using CLI Helper Functions: + - `otExecCommand()` - Executes CLI commands with error handling + - Commands: "dataset clear", "dataset networkkey", "dataset channel", "dataset commit active", "ifconfig up", "thread start" + - Waits for device to become Router or Child (with 90-second timeout) + - Demonstrates dual API usage for getting PAN ID + +2. **`loop()`**: + - Displays comprehensive network information using `OpenThread.otPrintNetworkInformation()` + - Updates every 10 seconds + - Shows error message if setup failed + +## Troubleshooting + +- **Device not joining network**: Ensure the Leader node is running first. Verify network key and channel match the Leader exactly. +- **Timeout error**: The device waits 90 seconds to join. If timeout occurs, check network key and channel match the Leader. +- **Network key/channel mismatch**: Double-check that both Leader and Router/Child nodes use identical network key and channel values. +- **Setup failed message**: Check Serial Monitor for specific error messages. Verify Leader node is running and within range. +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.yml b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/ExtendedRouterNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino index a945a5c8f77..b2a1330b93e 100644 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/LeaderNode.ino @@ -28,8 +28,8 @@ #include "OThreadCLI.h" #include "OThreadCLI_Util.h" -#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" -#define CLI_NETWORK_CHANEL "dataset channel 24" +#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "dataset channel 24" otInstance *aInstance = NULL; @@ -43,7 +43,7 @@ void setup() { OThreadCLI.println("dataset init new"); OThreadCLI.println(CLI_NETWORK_KEY); - OThreadCLI.println(CLI_NETWORK_CHANEL); + OThreadCLI.println(CLI_NETWORK_CHANNEL); OThreadCLI.println("dataset commit active"); OThreadCLI.println("ifconfig up"); OThreadCLI.println("thread start"); diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/README.md b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/README.md new file mode 100644 index 00000000000..6874ab2fde0 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/README.md @@ -0,0 +1,150 @@ +# OpenThread Leader Node Example (CLI) + +This example demonstrates how to create an OpenThread Leader node using the CLI Helper Functions API.\ +The Leader node is the first device in a Thread network that manages the network and assigns router IDs. This example shows how to configure a Leader node manually using OpenThread CLI commands. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin(false)` which does not automatically start a Thread network, allowing manual configuration. + +## Features + +- Manual Leader node configuration using CLI Helper Functions API +- Complete dataset initialization with network key and channel +- Network information display using native OpenThread API calls +- Demonstrates both CLI Helper Functions and native OpenThread API usage +- IPv6 address listing (unicast and multicast) + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, you can modify the network configuration: + +```cpp +#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "dataset channel 24" +``` + +**Important:** +- The network key must be a 32-character hexadecimal string (16 bytes) +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) +- All devices in the same network must use the same network key and channel + +## Building and Flashing + +1. Open the `LeaderNode.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +Setting up OpenThread Node as Leader +============================================= +Thread Node State: Leader +Network Name: OpenThread-ESP +Channel: 24 +PanID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +IP Address: fd00:db8:a0:0:0:ff:fe00:0 +Multicast IP Address: ff02::1 +Multicast IP Address: ff03::1 +... +``` + +## Using the Device + +### Leader Node Setup + +The Leader node is automatically configured in `setup()` using the following sequence: + +1. **Initialize new dataset**: Creates a complete dataset with random values +2. **Set network key**: Configures the network security key +3. **Set channel**: Configures the IEEE 802.15.4 channel +4. **Commit dataset**: Applies the dataset to the active configuration +5. **Start interface**: Brings up the network interface +6. **Start Thread**: Starts the Thread network + +### Network Information + +Once the device becomes a Leader, the `loop()` function displays: +- Device role (Leader) +- Network name +- Channel +- PAN ID and Extended PAN ID +- Network key +- All IPv6 addresses (unicast and multicast) + +### Joining Other Devices + +To join other devices to this network: +1. Use the same network key and channel in the Router/Child node examples +2. Start the Leader node first +3. Then start the Router/Child nodes + +## Code Structure + +The LeaderNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` (no auto-start) + - Initializes OpenThread CLI + - Configures the device as a Leader using CLI Helper Functions: + - `OThreadCLI.println()` - Sends CLI commands + - Commands: "dataset init new", "dataset networkkey", "dataset channel", "dataset commit active", "ifconfig up", "thread start" + +2. **`loop()`**: + - Checks if device role is Leader using `OpenThread.otGetDeviceRole()` + - Displays network information using native OpenThread API calls: + - `otThreadGetNetworkName()` - Network name + - `otLinkGetChannel()` - Channel + - `otLinkGetPanId()` - PAN ID + - `otThreadGetExtendedPanId()` - Extended PAN ID + - `otThreadGetNetworkKey()` - Network key + - `otIp6GetUnicastAddresses()` - Unicast IPv6 addresses + - `otIp6GetMulticastAddresses()` - Multicast IPv6 addresses + - Updates every 5 seconds + +## Troubleshooting + +- **Device not becoming Leader**: Ensure this is the first device started, or clear NVS to start fresh +- **Network key/channel mismatch**: Verify all devices use the same network key and channel +- **No network information**: Wait for the device to become Leader (may take a few seconds) +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.yml b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/LeaderNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/README.md b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/README.md new file mode 100644 index 00000000000..c9940fb7abc --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/README.md @@ -0,0 +1,162 @@ +# OpenThread Router/Child Node Example (CLI) + +This example demonstrates how to create an OpenThread Router or Child node that joins an existing Thread network using the CLI Helper Functions API.\ +The Router/Child node joins a network created by a Leader node and can route messages or operate as an end device. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin(false)` which does not automatically start a Thread network, allowing manual configuration. +- **Important:** A Leader node must be running before starting this Router/Child node. + +## Features + +- Manual Router/Child node configuration using CLI Helper Functions API +- Joins an existing Thread network created by a Leader node +- Network information display using native OpenThread API calls +- Demonstrates both CLI Helper Functions and native OpenThread API usage +- IPv6 address listing (unicast and multicast) +- Automatic role assignment (Router or Child based on network conditions) + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication +- A Leader node must be running on the same network + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, configure the network parameters to match the Leader node: + +```cpp +#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "dataset channel 24" +``` + +**Important:** +- The network key **must match** the Leader node's network key +- The channel **must match** the Leader node's channel +- The network key must be a 32-character hexadecimal string (16 bytes) +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) + +## Building and Flashing + +1. **First, start the Leader node** using the LeaderNode example +2. Open the `RouterNode.ino` sketch in the Arduino IDE. +3. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +4. Connect your ESP32 board to your computer via USB. +5. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +Setting up OpenThread Node as Router/Child +Make sure the Leader Node is already running +============================================= +Thread Node State: Router +Network Name: OpenThread-ESP +Channel: 24 +PanID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +IP Address: fd00:db8:a0:0:0:ff:fe00:fc00 +Multicast IP Address: ff02::1 +Multicast IP Address: ff03::1 +... +``` + +The device will join as either a **Router** (if network needs more routers) or **Child** (end device). + +## Using the Device + +### Router/Child Node Setup + +The Router/Child node is automatically configured in `setup()` using the following sequence: + +1. **Clear dataset**: Clears any existing dataset +2. **Set network key**: Configures the network security key (must match Leader) +3. **Set channel**: Configures the IEEE 802.15.4 channel (must match Leader) +4. **Commit dataset**: Applies the dataset to the active configuration +5. **Start interface**: Brings up the network interface +6. **Start Thread**: Starts the Thread network and joins the existing network + +### Network Information + +Once the device joins the network (as Router or Child), the `loop()` function displays: +- Device role (Router or Child) +- Network name (from the Leader) +- Channel +- PAN ID and Extended PAN ID +- Network key +- All IPv6 addresses (unicast and multicast) + +### Multi-Device Network + +To create a multi-device Thread network: + +1. Start the Leader node first (using LeaderNode example) +2. Start Router/Child nodes (using this example) +3. All devices will form a mesh network +4. Routers extend network range and route messages +5. Children are end devices that can sleep + +## Code Structure + +The RouterNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` (no auto-start) + - Initializes OpenThread CLI + - Configures the device to join an existing network using CLI Helper Functions: + - `OThreadCLI.println()` - Sends CLI commands + - Commands: "dataset clear", "dataset networkkey", "dataset channel", "dataset commit active", "ifconfig up", "thread start" + +2. **`loop()`**: + - Checks if device role is Router or Child using `OpenThread.otGetDeviceRole()` + - Displays network information using native OpenThread API calls: + - `otThreadGetNetworkName()` - Network name + - `otLinkGetChannel()` - Channel + - `otLinkGetPanId()` - PAN ID + - `otThreadGetExtendedPanId()` - Extended PAN ID + - `otThreadGetNetworkKey()` - Network key + - `otIp6GetUnicastAddresses()` - Unicast IPv6 addresses + - `otIp6GetMulticastAddresses()` - Multicast IPv6 addresses + - Updates every 5 seconds + +## Troubleshooting + +- **Device not joining network**: Ensure the Leader node is running first. Verify network key and channel match the Leader exactly. +- **Role stuck as "Detached"**: Wait a few seconds for the device to join. Check that network key and channel match the Leader. +- **Network key/channel mismatch**: Double-check that both Leader and Router/Child nodes use identical network key and channel values. +- **No network information**: Wait for the device to join the network (may take 10-30 seconds) +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino index f802bd7ef01..e06832a5cfc 100644 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/RouterNode.ino @@ -27,8 +27,8 @@ #include "OThreadCLI.h" #include "OThreadCLI_Util.h" -#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" -#define CLI_NETWORK_CHANEL "dataset channel 24" +#define CLI_NETWORK_KEY "dataset networkkey 00112233445566778899aabbccddeeff" +#define CLI_NETWORK_CHANNEL "dataset channel 24" otInstance *aInstance = NULL; @@ -43,7 +43,7 @@ void setup() { OThreadCLI.println("dataset clear"); OThreadCLI.println(CLI_NETWORK_KEY); - OThreadCLI.println(CLI_NETWORK_CHANEL); + OThreadCLI.println(CLI_NETWORK_CHANNEL); OThreadCLI.println("dataset commit active"); OThreadCLI.println("ifconfig up"); OThreadCLI.println("thread start"); diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.json b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.yml b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/SimpleThreadNetwork/RouterNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/ThreadScan/README.md b/libraries/OpenThread/examples/CLI/ThreadScan/README.md new file mode 100644 index 00000000000..def249163bd --- /dev/null +++ b/libraries/OpenThread/examples/CLI/ThreadScan/README.md @@ -0,0 +1,137 @@ +# OpenThread Thread Scan Example + +This example demonstrates how to scan for IEEE 802.15.4 devices and Thread networks using OpenThread CLI commands.\ +The application continuously scans for nearby devices and Thread networks, showing both raw IEEE 802.15.4 scans and Thread-specific discovery scans. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin(true)` which automatically starts a Thread network, required for Thread discovery scans. + +## Features + +- IEEE 802.15.4 device scanning (works even when Thread is not started) +- Thread network discovery scanning (requires device to be in Child, Router, or Leader state) +- Continuous scanning with configurable intervals +- Demonstrates CLI Helper Functions API for scanning +- Useful for network discovery and debugging + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +No configuration is required before uploading the sketch. The example automatically starts a Thread network for discovery scanning. + +## Building and Flashing + +1. Open the `ThreadScan.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +This sketch will continuously scan the Thread Local Network and all devices IEEE 802.15.4 compatible + +Scanning for nearby IEEE 802.15.4 devices: +| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI | ++---+------------------+------------------+------+------------------+----+-----+-----+ +| 0 | OpenThread-ESP | dead00beef00cafe | 1234 | 1234567890abcdef | 24 | -45 | 255 | +Done + +Scanning MLE Discover: +| J | Network Name | Extended PAN | PAN | MAC Address | Ch | dBm | LQI | ++---+------------------+------------------+------+------------------+----+-----+-----+ +| 0 | OpenThread-ESP | dead00beef00cafe | 1234 | 1234567890abcdef | 24 | -45 | 255 | +Done +``` + +## Using the Device + +### Scanning Behavior + +The example performs two types of scans: + +1. **IEEE 802.15.4 Scan**: + - Scans for all IEEE 802.15.4 compatible devices in the area + - Works even when the device is not part of a Thread network + - Shows devices on all channels + +2. **Thread Discovery Scan (MLE Discover)**: + - Scans for Thread networks specifically + - Only works when the device is in Child, Router, or Leader state + - Shows Thread networks with their network names and parameters + +### Scan Results + +The scan results show: +- **J**: Joinable flag (1 = can join, 0 = cannot join) +- **Network Name**: Thread network name +- **Extended PAN**: Extended PAN ID +- **PAN**: PAN ID +- **MAC Address**: Device MAC address +- **Ch**: Channel +- **dBm**: Signal strength in dBm +- **LQI**: Link Quality Indicator + +### Continuous Scanning + +The example continuously scans: +- IEEE 802.15.4 scan every 5 seconds +- Thread discovery scan every 5 seconds (only if device is in Thread network) + +## Code Structure + +The ThreadScan example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(true)` (auto-start required for discovery) + - Initializes OpenThread CLI + - Sets CLI timeout to 100 ms using `OThreadCLI.setTimeout()` + +2. **`loop()`**: + - Performs IEEE 802.15.4 scan using `otPrintRespCLI("scan", Serial, 3000)` + - Checks if device is in Thread network (Child, Router, or Leader) + - If in network, performs Thread discovery scan using `otPrintRespCLI("discover", Serial, 3000)` + - Waits 5 seconds between scan cycles + +## Troubleshooting + +- **No scan results**: Ensure there are Thread devices nearby. Check that other devices are running and on the same channel. +- **Discovery scan not working**: Wait for the device to join a Thread network (should become Child, Router, or Leader) +- **Scan timeout**: Increase the timeout value in `otPrintRespCLI()` if scans are taking longer +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/ThreadScan/ci.json b/libraries/OpenThread/examples/CLI/ThreadScan/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/ThreadScan/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/ThreadScan/ci.yml b/libraries/OpenThread/examples/CLI/ThreadScan/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/ThreadScan/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/CLI/onReceive/README.md b/libraries/OpenThread/examples/CLI/onReceive/README.md new file mode 100644 index 00000000000..c2f43729aaf --- /dev/null +++ b/libraries/OpenThread/examples/CLI/onReceive/README.md @@ -0,0 +1,130 @@ +# OpenThread CLI onReceive Callback Example + +This example demonstrates how to use the OpenThread CLI callback mechanism to capture and process CLI responses asynchronously.\ +The application shows how to set up a callback function that processes CLI responses line by line, allowing non-blocking CLI interaction. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses `OpenThread.begin()` which automatically starts a Thread network with default settings. + +## Features + +- CLI response callback using `OpenThreadCLI.onReceive()` +- Asynchronous CLI response processing +- Non-blocking CLI command execution +- Demonstrates callback-based CLI interaction pattern +- Automatic Thread network startup with default settings +- Device role monitoring via CLI + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +No configuration is required before uploading the sketch. The example automatically starts a Thread network with default settings. + +## Building and Flashing + +1. Open the `onReceive.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +OpenThread CLI RESP===> disabled +OpenThread CLI RESP===> disabled +OpenThread CLI RESP===> detached +OpenThread CLI RESP===> child +OpenThread CLI RESP===> router +OpenThread CLI RESP===> router +... +``` + +The callback function processes each line of CLI response, showing the device state transitions from "disabled" to "detached" to "child" to "router" (or "leader"). + +## Using the Device + +### Callback Mechanism + +The example demonstrates the callback-based CLI interaction pattern: + +1. **Callback Registration**: `OThreadCLI.onReceive(otReceivedLine)` registers a callback function +2. **Command Execution**: `OThreadCLI.println("state")` sends CLI commands +3. **Response Processing**: The callback function `otReceivedLine()` processes responses asynchronously +4. **Non-blocking**: The main loop continues while CLI responses are processed in the callback + +### Device State Monitoring + +The example continuously monitors the device state: +- Sends "state" command every second +- Callback processes the response +- Shows state transitions as the device joins the Thread network + +### Customizing the Callback + +You can modify the `otReceivedLine()` function to: +- Parse specific CLI responses +- Extract data from CLI output +- Trigger actions based on CLI responses +- Filter or process specific response patterns + +## Code Structure + +The onReceive example consists of the following main components: + +1. **`otReceivedLine()` callback function**: + - Reads all available data from OpenThread CLI + - Filters out empty lines (EOL sequences) + - Prints non-empty lines with a prefix + +2. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin()` (auto-start) + - Initializes OpenThread CLI + - Registers the callback function using `OThreadCLI.onReceive(otReceivedLine)` + +3. **`loop()`**: + - Sends "state" CLI command every second using `OThreadCLI.println("state")` + - The callback function processes the response asynchronously + - Non-blocking operation allows other tasks to run + +## Troubleshooting + +- **No callback responses**: Ensure the callback is registered in setup. Check that OpenThread CLI is initialized. +- **Empty lines in output**: The callback filters empty lines, which is normal behavior +- **State not changing**: Wait for the device to join the Thread network. First device becomes Leader, subsequent devices become Router or Child. +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread CLI Helper Functions API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_cli.html) +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/CLI/onReceive/ci.json b/libraries/OpenThread/examples/CLI/onReceive/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/CLI/onReceive/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/CLI/onReceive/ci.yml b/libraries/OpenThread/examples/CLI/onReceive/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/CLI/onReceive/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/README.md b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/README.md new file mode 100644 index 00000000000..37fbe871033 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/README.md @@ -0,0 +1,183 @@ +# OpenThread Leader Node Example (Native API) + +This example demonstrates how to create an OpenThread Leader node using the Classes API (native OpenThread API).\ +The Leader node is the first device in a Thread network that manages the network and assigns router IDs. This example shows how to configure a Leader node using the `OpenThread` and `DataSet` classes. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses the Classes API (`OpenThread` and `DataSet` classes) instead of CLI Helper Functions. +- This example uses `OpenThread.begin(false)` which does not use NVS dataset information, allowing fresh configuration. + +## Features + +- Leader node configuration using Classes API +- Dataset creation and configuration using `DataSet` class +- Network information display using `OpenThread` class methods +- IPv6 address management (unicast and multicast) +- Address cache management on role changes +- Comprehensive network status monitoring + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, you can modify the network configuration: + +```cpp +dataset.setNetworkName("ESP_OpenThread"); +dataset.setChannel(15); +dataset.setPanId(0x1234); +uint8_t networkKey[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; +dataset.setNetworkKey(networkKey); +``` + +**Important:** +- The network key must be a 16-byte array +- The channel must be between 11 and 26 (IEEE 802.15.4 channels) +- All devices in the same network must use the same network key and channel +- Extended PAN ID should be unique for your network + +## Building and Flashing + +1. Open the `LeaderNode.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +3. Connect your ESP32 board to your computer via USB. +4. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +============================================== +OpenThread Network Information: +Role: Leader +RLOC16: 0x0000 +Network Name: ESP_OpenThread +Channel: 15 +PAN ID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +Mesh Local EID: fd00:db8:a0:0:0:ff:fe00:0 +Leader RLOC: fd00:db8:a0:0:0:ff:fe00:0 +Node RLOC: fd00:db8:a0:0:0:ff:fe00:0 + +--- Unicast Addresses (Using Count + Index API) --- + [0]: fd00:db8:a0:0:0:ff:fe00:0 + [1]: fe80:0:0:0:0:ff:fe00:0 + +--- Multicast Addresses (Using std::vector API) --- + [0]: ff02::1 + [1]: ff03::1 + [2]: ff03::fc +... +``` + +## Using the Device + +### Leader Node Setup + +The Leader node is automatically configured in `setup()` using the Classes API: + +1. **Initialize OpenThread**: `threadLeaderNode.begin(false)` - Starts OpenThread stack without using NVS +2. **Create dataset**: `dataset.initNew()` - Creates a new complete dataset +3. **Configure dataset**: Sets network name, extended PAN ID, network key, channel, and PAN ID +4. **Apply dataset**: `threadLeaderNode.commitDataSet(dataset)` - Applies the dataset +5. **Start network**: `threadLeaderNode.networkInterfaceUp()` and `threadLeaderNode.start()` - Starts the Thread network + +### Network Information + +The `loop()` function displays comprehensive network information using Classes API methods: +- Device role and RLOC16 +- Network name, channel, PAN ID +- Extended PAN ID and network key +- IPv6 addresses (Mesh Local EID, Leader RLOC, Node RLOC) +- Unicast addresses (using count + index API) +- Multicast addresses (using std::vector API) + +### Address Cache Management + +The example demonstrates address cache management: +- Clears address cache when device role changes +- Tracks role changes to optimize address resolution + +### Joining Other Devices + +To join other devices to this network: +1. Use the same network key and channel in the Router/Child node examples +2. Start the Leader node first +3. Then start the Router/Child nodes + +## Code Structure + +The LeaderNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` + - Creates and configures a `DataSet` object: + - `dataset.initNew()` - Initialize new dataset + - `dataset.setNetworkName()` - Set network name + - `dataset.setExtendedPanId()` - Set extended PAN ID + - `dataset.setNetworkKey()` - Set network key + - `dataset.setChannel()` - Set channel + - `dataset.setPanId()` - Set PAN ID + - Applies dataset: `threadLeaderNode.commitDataSet(dataset)` + - Starts network: `threadLeaderNode.networkInterfaceUp()` and `threadLeaderNode.start()` + +2. **`loop()`**: + - Gets current device role using `threadLeaderNode.otGetDeviceRole()` + - Displays network information using Classes API methods: + - `threadLeaderNode.otGetStringDeviceRole()` - Device role as string + - `threadLeaderNode.getRloc16()` - RLOC16 + - `threadLeaderNode.getNetworkName()` - Network name + - `threadLeaderNode.getChannel()` - Channel + - `threadLeaderNode.getPanId()` - PAN ID + - `threadLeaderNode.getExtendedPanId()` - Extended PAN ID + - `threadLeaderNode.getNetworkKey()` - Network key + - `threadLeaderNode.getMeshLocalEid()` - Mesh Local EID + - `threadLeaderNode.getLeaderRloc()` - Leader RLOC + - `threadLeaderNode.getRloc()` - Node RLOC + - `threadLeaderNode.getUnicastAddressCount()` and `threadLeaderNode.getUnicastAddress(i)` - Unicast addresses + - `threadLeaderNode.getAllMulticastAddresses()` - Multicast addresses + - Manages address cache on role changes + - Updates every 5 seconds + +## Troubleshooting + +- **Device not becoming Leader**: Ensure this is the first device started, or clear NVS to start fresh +- **Network key/channel mismatch**: Verify all devices use the same network key and channel +- **No network information**: Wait for the device to become Leader (may take a few seconds) +- **Address cache issues**: The example automatically clears cache on role changes +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Dataset API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_dataset.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.yml b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/LeaderNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/README.md b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/README.md new file mode 100644 index 00000000000..90a56db2e69 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/README.md @@ -0,0 +1,174 @@ +# OpenThread Router/Child Node Example (Native API) + +This example demonstrates how to create an OpenThread Router or Child node that joins an existing Thread network using the Classes API (native OpenThread API).\ +The Router/Child node joins a network created by a Leader node and can route messages or operate as an end device. This example shows how to configure a Router/Child node using the `OpenThread` and `DataSet` classes. + +## Supported Targets + +| SoC | Thread | Status | +| --- | ------ | ------ | +| ESP32-H2 | ✅ | Fully supported | +| ESP32-C6 | ✅ | Fully supported | +| ESP32-C5 | ✅ | Fully supported | + +### Note on Thread Support: + +- Thread support must be enabled in the ESP-IDF configuration (`CONFIG_OPENTHREAD_ENABLED`). This is done automatically when using the ESP32 Arduino OpenThread library. +- This example uses the Classes API (`OpenThread` and `DataSet` classes) instead of CLI Helper Functions. +- This example uses `OpenThread.begin(false)` which does not use NVS dataset information, allowing fresh configuration. +- **Important:** A Leader node must be running before starting this Router/Child node. + +## Features + +- Router/Child node configuration using Classes API +- Dataset configuration using `DataSet` class +- Joins an existing Thread network created by a Leader node +- Network information display using `OpenThread` class methods +- Active dataset retrieval and display +- Comprehensive network status monitoring + +## Hardware Requirements + +- ESP32 compatible development board with Thread support (ESP32-H2, ESP32-C6, or ESP32-C5) +- USB cable for Serial communication +- A Leader node must be running on the same network + +## Software Setup + +### Prerequisites + +1. Install the Arduino IDE (2.0 or newer recommended) +2. Install ESP32 Arduino Core with OpenThread support +3. ESP32 Arduino libraries: + - `OpenThread` + +### Configuration + +Before uploading the sketch, configure the network parameters to match the Leader node: + +```cpp +uint8_t networkKey[OT_NETWORK_KEY_SIZE] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; +dataset.setNetworkKey(networkKey); +``` + +**Important:** +- The network key **must match** the Leader node's network key exactly +- The network key must be a 16-byte array +- Only the network key is required to join (other parameters are learned from the Leader) +- **Start the Leader node first** before starting this Router/Child node + +## Building and Flashing + +1. **First, start the Leader node** using the LeaderNode example (Native API) +2. Open the `RouterNode.ino` sketch in the Arduino IDE. +3. Select your ESP32 board from the **Tools > Board** menu (ESP32-H2, ESP32-C6, or ESP32-C5). +4. Connect your ESP32 board to your computer via USB. +5. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +Once the sketch is running, open the Serial Monitor at a baud rate of **115200**. You should see output similar to the following: + +``` +============================================== +OpenThread Network Information (Active Dataset): +Role: Router +RLOC16: 0xfc00 +Network Name: ESP_OpenThread +Channel: 15 +PAN ID: 0x1234 +Extended PAN ID: dead00beef00cafe +Network Key: 00112233445566778899aabbccddeeff +Mesh Local EID: fd00:db8:a0:0:0:ff:fe00:fc00 +Node RLOC: fd00:db8:a0:0:0:ff:fe00:fc00 +Leader RLOC: fd00:db8:a0:0:0:ff:fe00:0 +``` + +The device will join as either a **Router** (if network needs more routers) or **Child** (end device). + +## Using the Device + +### Router/Child Node Setup + +The Router/Child node is automatically configured in `setup()` using the Classes API: + +1. **Initialize OpenThread**: `threadChildNode.begin(false)` - Starts OpenThread stack without using NVS +2. **Clear dataset**: `dataset.clear()` - Clears any existing dataset +3. **Configure dataset**: Sets only the network key (must match Leader) +4. **Apply dataset**: `threadChildNode.commitDataSet(dataset)` - Applies the dataset +5. **Start network**: `threadChildNode.networkInterfaceUp()` and `threadChildNode.start()` - Starts the Thread network and joins existing network + +### Network Information + +The `loop()` function displays network information using Classes API methods: +- Device role and RLOC16 +- Active dataset information (retrieved using `threadChildNode.getCurrentDataSet()`): + - Network name, channel, PAN ID + - Extended PAN ID and network key +- Runtime information: + - Mesh Local EID, Node RLOC, Leader RLOC + +### Active Dataset Retrieval + +This example demonstrates how to retrieve the active dataset: +- Uses `threadChildNode.getCurrentDataSet()` to get the current active dataset +- Displays dataset parameters that were learned from the Leader node +- Shows that only the network key needs to be configured to join + +### Multi-Device Network + +To create a multi-device Thread network: + +1. Start the Leader node first (using Native API LeaderNode example) +2. Start Router/Child nodes (using this example) +3. All devices will form a mesh network +4. Routers extend network range and route messages +5. Children are end devices that can sleep + +## Code Structure + +The RouterNode example consists of the following main components: + +1. **`setup()`**: + - Initializes Serial communication + - Starts OpenThread stack with `OpenThread.begin(false)` + - Creates and configures a `DataSet` object: + - `dataset.clear()` - Clear existing dataset + - `dataset.setNetworkKey()` - Set network key (must match Leader) + - Applies dataset: `threadChildNode.commitDataSet(dataset)` + - Starts network: `threadChildNode.networkInterfaceUp()` and `threadChildNode.start()` + +2. **`loop()`**: + - Gets current device role using `threadChildNode.otGetDeviceRole()` + - Retrieves active dataset using `threadChildNode.getCurrentDataSet()` + - Displays network information using Classes API methods: + - `threadChildNode.otGetStringDeviceRole()` - Device role as string + - `threadChildNode.getRloc16()` - RLOC16 + - `activeDataset.getNetworkName()` - Network name from dataset + - `activeDataset.getChannel()` - Channel from dataset + - `activeDataset.getPanId()` - PAN ID from dataset + - `activeDataset.getExtendedPanId()` - Extended PAN ID from dataset + - `activeDataset.getNetworkKey()` - Network key from dataset + - `threadChildNode.getMeshLocalEid()` - Mesh Local EID + - `threadChildNode.getRloc()` - Node RLOC + - `threadChildNode.getLeaderRloc()` - Leader RLOC + - Updates every 5 seconds + +## Troubleshooting + +- **Device not joining network**: Ensure the Leader node is running first. Verify network key matches the Leader exactly. +- **Role stuck as "Detached"**: Wait a few seconds for the device to join. Check that network key matches the Leader. +- **Network key mismatch**: Double-check that both Leader and Router/Child nodes use identical network key values. +- **No network information**: Wait for the device to join the network (may take 10-30 seconds) +- **Active dataset empty**: Ensure device has successfully joined the network before checking dataset +- **No serial output**: Check baudrate (115200) and USB connection + +## Related Documentation + +- [OpenThread Core API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_core.html) +- [OpenThread Dataset API](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread_dataset.html) +- [OpenThread Overview](https://docs.espressif.com/projects/arduino-esp32/en/latest/openthread/openthread.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json deleted file mode 100644 index 2ee6af3490e..00000000000 --- a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_OPENTHREAD_ENABLED=y", - "CONFIG_SOC_IEEE802154_SUPPORTED=y" - ] -} diff --git a/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.yml b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.yml new file mode 100644 index 00000000000..452905cdc41 --- /dev/null +++ b/libraries/OpenThread/examples/Native/SimpleThreadNetwork/RouterNode/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_OPENTHREAD_ENABLED=y + - CONFIG_SOC_IEEE802154_SUPPORTED=y diff --git a/libraries/OpenThread/library.properties b/libraries/OpenThread/library.properties index 550d4eb1627..68b9c85f864 100644 --- a/libraries/OpenThread/library.properties +++ b/libraries/OpenThread/library.properties @@ -1,5 +1,5 @@ name=OpenThread -version=3.3.0 +version=3.3.4 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for OpenThread Network on ESP32. diff --git a/libraries/OpenThread/src/OThread.h b/libraries/OpenThread/src/OThread.h index 6e21b854574..e477eec19fe 100644 --- a/libraries/OpenThread/src/OThread.h +++ b/libraries/OpenThread/src/OThread.h @@ -162,7 +162,9 @@ class OpenThread { void populateMulticastAddressCache() const; }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OPENTHREAD) extern OpenThread OThread; +#endif #endif /* CONFIG_OPENTHREAD_ENABLED */ #endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/OpenThread/src/OThreadCLI.h b/libraries/OpenThread/src/OThreadCLI.h index 788edc2709b..cb2f2717d4f 100644 --- a/libraries/OpenThread/src/OThreadCLI.h +++ b/libraries/OpenThread/src/OThreadCLI.h @@ -68,7 +68,9 @@ class OpenThreadCLI : public Stream { void flush(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OPENTHREADCLI) extern OpenThreadCLI OThreadCLI; +#endif #endif /* CONFIG_OPENTHREAD_ENABLED */ #endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/PPP/examples/PPP_Basic/ci.json b/libraries/PPP/examples/PPP_Basic/ci.json deleted file mode 100644 index 314587edcf6..00000000000 --- a/libraries/PPP/examples/PPP_Basic/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_LWIP_PPP_SUPPORT=y" - ] -} diff --git a/libraries/PPP/examples/PPP_Basic/ci.yml b/libraries/PPP/examples/PPP_Basic/ci.yml new file mode 100644 index 00000000000..857734a4d75 --- /dev/null +++ b/libraries/PPP/examples/PPP_Basic/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_LWIP_PPP_SUPPORT=y diff --git a/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.json b/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.json deleted file mode 100644 index ccc62161c85..00000000000 --- a/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "requires": [ - "CONFIG_LWIP_PPP_SUPPORT=y" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.yml b/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.yml new file mode 100644 index 00000000000..2e80e1b43ca --- /dev/null +++ b/libraries/PPP/examples/PPP_WIFI_BRIDGE/ci.yml @@ -0,0 +1,6 @@ +requires: + - CONFIG_LWIP_PPP_SUPPORT=y + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/PPP/library.properties b/libraries/PPP/library.properties index 537708b1261..b6ce0d16f34 100644 --- a/libraries/PPP/library.properties +++ b/libraries/PPP/library.properties @@ -1,5 +1,5 @@ name=PPP -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection using GSM Modem. diff --git a/libraries/PPP/src/PPP.cpp b/libraries/PPP/src/PPP.cpp index 2a5c5760287..01989429ae5 100644 --- a/libraries/PPP/src/PPP.cpp +++ b/libraries/PPP/src/PPP.cpp @@ -394,6 +394,11 @@ void PPPClass::end(void) { Network.postEvent(&arduino_event); } + if (_dce != NULL) { + esp_modem_destroy(_dce); + _dce = NULL; + } + destroyNetif(); if (_ppp_ev_instance != NULL) { @@ -406,10 +411,10 @@ void PPPClass::end(void) { Network.removeEvent(_ppp_event_handle); _ppp_event_handle = 0; - if (_dce != NULL) { - esp_modem_destroy(_dce); - _dce = NULL; - } + perimanClearBusDeinit(ESP32_BUS_TYPE_PPP_TX); + perimanClearBusDeinit(ESP32_BUS_TYPE_PPP_RX); + perimanClearBusDeinit(ESP32_BUS_TYPE_PPP_RTS); + perimanClearBusDeinit(ESP32_BUS_TYPE_PPP_CTS); int8_t pin = -1; if (_pin_tx != -1) { @@ -438,6 +443,11 @@ void PPPClass::end(void) { perimanClearPinBus(pin); } + perimanSetBusDeinit(ESP32_BUS_TYPE_PPP_TX, PPPClass::pppDetachBus); + perimanSetBusDeinit(ESP32_BUS_TYPE_PPP_RX, PPPClass::pppDetachBus); + perimanSetBusDeinit(ESP32_BUS_TYPE_PPP_RTS, PPPClass::pppDetachBus); + perimanSetBusDeinit(ESP32_BUS_TYPE_PPP_CTS, PPPClass::pppDetachBus); + _mode = ESP_MODEM_MODE_COMMAND; } @@ -747,6 +757,30 @@ String PPPClass::cmd(const char *at_command, int timeout) { return String(out); } +bool PPPClass::cmd(const char *at_command, String &response, int timeout) { + PPP_CMD_MODE_CHECK(false); + + char out[128] = {0}; + esp_err_t err = _esp_modem_at(_dce, at_command, out, timeout); + response = String(out); + + if (err != ESP_OK) { + log_e("esp_modem_at failed %d %s", err, esp_err_to_name(err)); + + if (err == ESP_FAIL && response.isEmpty()) { + response = "ERROR"; + } + + return false; + } + + if (response.isEmpty()) { + response = "OK"; + } + + return true; +} + size_t PPPClass::printDriverInfo(Print &out) const { size_t bytes = 0; if (_dce == NULL || _mode == ESP_MODEM_MODE_DATA) { @@ -763,6 +797,8 @@ size_t PPPClass::printDriverInfo(Print &out) const { return bytes; } +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_PPP) PPPClass PPP; +#endif #endif /* CONFIG_LWIP_PPP_SUPPORT */ diff --git a/libraries/PPP/src/PPP.h b/libraries/PPP/src/PPP.h index b317f52aefc..cb87b57fe57 100644 --- a/libraries/PPP/src/PPP.h +++ b/libraries/PPP/src/PPP.h @@ -73,11 +73,26 @@ class PPPClass : public NetworkInterface { } // Send AT command with timeout in milliseconds + // Function deprecated - kept for backward compatibility + // Function may return empty string in multiple cases: + // - When timeout occurred; + // - When "OK" AT response was received; + // - When "ERROR" AT response was received. + // For more detailed return, usage of `bool PPPClass::cmd(at_command, response, timeout)` is recommended. String cmd(const char *at_command, int timeout); String cmd(String at_command, int timeout) { return cmd(at_command.c_str(), timeout); } + // Send AT command with timeout in milliseconds + // When PPP is not started or timeout occurs: Function returns false; response string is not modified + // When AT error response is received: Function returns false; response contains "ERROR" or detailed AT response + // When AT success response is received: Function returns true; response contains "OK" or detailed AT response + bool cmd(const char *at_command, String &response, int timeout); + bool cmd(String at_command, String &response, int timeout) { + return cmd(at_command.c_str(), response, timeout); + } + // untested bool powerDown(); bool reset(); @@ -113,5 +128,8 @@ class PPPClass : public NetworkInterface { static bool pppDetachBus(void *bus_pointer); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_PPP) extern PPPClass PPP; +#endif + #endif /* CONFIG_LWIP_PPP_SUPPORT && ARDUINO_HAS_ESP_MODEM */ diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties index 0a7e678aa6c..a9e3932888e 100644 --- a/libraries/Preferences/library.properties +++ b/libraries/Preferences/library.properties @@ -1,5 +1,5 @@ name=Preferences -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides friendly access to ESP32's Non-Volatile Storage diff --git a/libraries/RainMaker/examples/RMakerCustom/ci.json b/libraries/RainMaker/examples/RMakerCustom/ci.json deleted file mode 100644 index ce63fe9ccf0..00000000000 --- a/libraries/RainMaker/examples/RMakerCustom/ci.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "targets": { - "esp32": false - }, - "fqbn_append": "PartitionScheme=rainmaker_4MB", - "requires": [ - "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/RainMaker/examples/RMakerCustom/ci.yml b/libraries/RainMaker/examples/RMakerCustom/ci.yml new file mode 100644 index 00000000000..fc2a11e6948 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerCustom/ci.yml @@ -0,0 +1,11 @@ +targets: + esp32: false + +fqbn_append: PartitionScheme=rainmaker_4MB + +requires: + - CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]* + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json b/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json deleted file mode 100644 index ce63fe9ccf0..00000000000 --- a/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "targets": { - "esp32": false - }, - "fqbn_append": "PartitionScheme=rainmaker_4MB", - "requires": [ - "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.yml b/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.yml new file mode 100644 index 00000000000..fc2a11e6948 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerCustomAirCooler/ci.yml @@ -0,0 +1,11 @@ +targets: + esp32: false + +fqbn_append: PartitionScheme=rainmaker_4MB + +requires: + - CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]* + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json b/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json deleted file mode 100644 index ce63fe9ccf0..00000000000 --- a/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "targets": { - "esp32": false - }, - "fqbn_append": "PartitionScheme=rainmaker_4MB", - "requires": [ - "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.yml b/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.yml new file mode 100644 index 00000000000..fc2a11e6948 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerSonoffDualR3/ci.yml @@ -0,0 +1,11 @@ +targets: + esp32: false + +fqbn_append: PartitionScheme=rainmaker_4MB + +requires: + - CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]* + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/RainMaker/examples/RMakerSwitch/ci.json b/libraries/RainMaker/examples/RMakerSwitch/ci.json deleted file mode 100644 index ce63fe9ccf0..00000000000 --- a/libraries/RainMaker/examples/RMakerSwitch/ci.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "targets": { - "esp32": false - }, - "fqbn_append": "PartitionScheme=rainmaker_4MB", - "requires": [ - "CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]*" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/RainMaker/examples/RMakerSwitch/ci.yml b/libraries/RainMaker/examples/RMakerSwitch/ci.yml new file mode 100644 index 00000000000..fc2a11e6948 --- /dev/null +++ b/libraries/RainMaker/examples/RMakerSwitch/ci.yml @@ -0,0 +1,11 @@ +targets: + esp32: false + +fqbn_append: PartitionScheme=rainmaker_4MB + +requires: + - CONFIG_ESP_RMAKER_WORK_QUEUE_TASK_STACK=[1-9][0-9]* + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/RainMaker/library.properties b/libraries/RainMaker/library.properties index 1d72a8faff5..af0c63b233b 100644 --- a/libraries/RainMaker/library.properties +++ b/libraries/RainMaker/library.properties @@ -1,5 +1,5 @@ name=ESP RainMaker -version=3.3.0 +version=3.3.4 author=Sweety Mhaiske maintainer=Hristo Gochkov sentence=ESP RainMaker Support diff --git a/libraries/RainMaker/src/RMaker.h b/libraries/RainMaker/src/RMaker.h index 21c5b0d1832..0e16e6ee387 100644 --- a/libraries/RainMaker/src/RMaker.h +++ b/libraries/RainMaker/src/RMaker.h @@ -39,5 +39,8 @@ class RMakerClass { esp_err_t stop(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_RAINMAKER) extern RMakerClass RMaker; #endif + +#endif diff --git a/libraries/SD/examples/SD_time/SD_time.ino b/libraries/SD/examples/SD_time/SD_time.ino index cc65d21ae20..48d119bebc4 100644 --- a/libraries/SD/examples/SD_time/SD_time.ino +++ b/libraries/SD/examples/SD_time/SD_time.ino @@ -78,10 +78,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -92,10 +93,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SD/examples/SD_time/ci.json b/libraries/SD/examples/SD_time/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/SD/examples/SD_time/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/SD/examples/SD_time/ci.yml b/libraries/SD/examples/SD_time/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/SD/examples/SD_time/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/SD/library.properties b/libraries/SD/library.properties index 9d868dce799..4bb9876e684 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,5 +1,5 @@ name=SD -version=3.3.0 +version=3.3.4 author=Arduino, SparkFun maintainer=Arduino sentence=Enables reading and writing on SD cards. For all Arduino boards. diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h index d8252ee44f7..f4311d25a11 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -43,7 +43,9 @@ class SDFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD) extern fs::SDFS SD; +#endif using namespace fs; typedef fs::File SDFile; diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp index 40d6ede9f81..f33c5c79b12 100644 --- a/libraries/SD/src/sd_diskio.cpp +++ b/libraries/SD/src/sd_diskio.cpp @@ -462,23 +462,52 @@ struct AcquireSPI { * FATFS API * */ +/** + * @brief Initialize an SD card for use with FatFs + * + * This function implements the complete SD card initialization sequence according to + * the SD card specification. It performs card detection, type identification, + * and configuration for SPI mode operation. + * + * The initialization sequence follows the SD card protocol: + * 1. Power-up sequence with 74+ clock cycles + * 2. GO_IDLE_STATE command to reset the card + * 3. CRC_ON_OFF to enable/disable CRC checking + * 4. SEND_IF_COND to identify SDHC/SDXC cards + * 5. APP_OP_COND to set operating conditions + * 6. Card type detection (SD/SDHC/MMC) + * 7. Final configuration and sector count retrieval + * + * @param pdrv Physical drive number (0-9) + * @return DSTATUS Status of the initialization (0 = success, STA_NOINIT = failed) + */ DSTATUS ff_sd_initialize(uint8_t pdrv) { char token; unsigned int resp; unsigned int start; + + // Get the card structure for the given drive number ardu_sdcard_t *card = s_cards[pdrv]; + // If the card is already initialized, return its current status if (!(card->status & STA_NOINIT)) { return card->status; } + // Lock the SPI bus and set it to a low frequency (400kHz) for initialization + // Low frequency is required during initialization for reliable communication AcquireSPI card_locked(card, 400000); + // Step 1: Power-up sequence - Send at least 74 clock cycles with CS high and MOSI high + // This is required by the SD card specification to ensure proper card state reset + // We send 20 bytes (160 clock cycles) to exceed the minimum requirement digitalWrite(card->ssPin, HIGH); for (uint8_t i = 0; i < 20; i++) { card->spi->transfer(0XFF); } + // Step 2: Select the card and send GO_IDLE_STATE command + // This command resets the card to idle state and enables SPI mode // Fix mount issue - sdWait fail ignored before command GO_IDLE_STATE digitalWrite(card->ssPin, LOW); if (!sdWait(pdrv, 500)) { @@ -491,26 +520,34 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } sdDeselectCard(pdrv); + // Step 3: Configure CRC checking + // Enable CRC for data transfers in SPI mode (required for reliable communication) token = sdTransaction(pdrv, CRC_ON_OFF, 1, NULL); if (token == 0x5) { - //old card maybe + // Old card that doesn't support CRC - disable CRC checking card->supports_crc = false; } else if (token != 1) { log_w("CRC_ON_OFF failed: %u", token); goto unknown_card; } + // Step 4: Card type detection and initialization + // Try to identify SDHC/SDXC cards using SEND_IF_COND command if (sdTransaction(pdrv, SEND_IF_COND, 0x1AA, &resp) == 1) { + // Card responded to SEND_IF_COND - likely SDHC/SDXC if ((resp & 0xFFF) != 0x1AA) { log_w("SEND_IF_COND failed: %03X", resp & 0xFFF); goto unknown_card; } + // Read Operating Conditions Register to check card capabilities if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { log_w("READ_OCR failed: %X", resp); goto unknown_card; } + // Send APP_OP_COND to set operating conditions for SDHC/SDXC + // Wait up to 1 second for the card to become ready start = millis(); do { token = sdTransaction(pdrv, APP_OP_COND, 0x40100000, NULL); @@ -521,37 +558,41 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { goto unknown_card; } + // Determine if it's SDHC (high capacity) or regular SD if (!sdTransaction(pdrv, READ_OCR, 0, &resp)) { if (resp & (1 << 30)) { - card->type = CARD_SDHC; + card->type = CARD_SDHC; // High capacity card (SDHC/SDXC) } else { - card->type = CARD_SD; + card->type = CARD_SD; // Standard capacity card } } else { log_w("READ_OCR failed: %X", resp); goto unknown_card; } } else { + // Card didn't respond to SEND_IF_COND - try SD or MMC initialization if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { log_w("READ_OCR failed: %X", resp); goto unknown_card; } + // Try SD card initialization first start = millis(); do { token = sdTransaction(pdrv, APP_OP_COND, 0x100000, NULL); } while (token == 0x01 && (millis() - start) < 1000); if (!token) { - card->type = CARD_SD; + card->type = CARD_SD; // Standard SD card } else { + // Try MMC card initialization start = millis(); do { token = sdTransaction(pdrv, SEND_OP_COND, 0x100000, NULL); } while (token != 0x00 && (millis() - start) < 1000); if (token == 0x00) { - card->type = CARD_MMC; + card->type = CARD_MMC; // MMC card } else { log_w("SEND_OP_COND failed: %u", token); goto unknown_card; @@ -559,6 +600,7 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 5: Clear card detection for SD cards (not needed for MMC) if (card->type != CARD_MMC) { if (sdTransaction(pdrv, APP_CLR_CARD_DETECT, 0, NULL)) { log_w("APP_CLR_CARD_DETECT failed"); @@ -566,6 +608,8 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 6: Set block length for non-SDHC cards + // SDHC cards have fixed 512-byte blocks, others need explicit block length setting if (card->type != CARD_SDHC) { if (sdTransaction(pdrv, SET_BLOCKLEN, 512, NULL) != 0x00) { log_w("SET_BLOCKLEN failed"); @@ -573,16 +617,20 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 7: Get card capacity and finalize initialization card->sectors = sdGetSectorsCount(pdrv); + // Limit frequency to 25MHz for compatibility (SD spec maximum for non-UHS cards) if (card->frequency > 25000000) { card->frequency = 25000000; } + // Mark card as initialized card->status &= ~STA_NOINIT; return card->status; unknown_card: + // Mark card as unknown type if initialization failed card->type = CARD_UNKNOWN; return card->status; } diff --git a/libraries/SD_MMC/examples/SD2USBMSC/ci.json b/libraries/SD_MMC/examples/SD2USBMSC/ci.json deleted file mode 100644 index 125eed2e476..00000000000 --- a/libraries/SD_MMC/examples/SD2USBMSC/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_SDMMC_HOST_SUPPORTED=y", - "CONFIG_TINYUSB_MSC_ENABLED=y" - ] -} diff --git a/libraries/SD_MMC/examples/SD2USBMSC/ci.yml b/libraries/SD_MMC/examples/SD2USBMSC/ci.yml new file mode 100644 index 00000000000..e2806a2b62c --- /dev/null +++ b/libraries/SD_MMC/examples/SD2USBMSC/ci.yml @@ -0,0 +1,3 @@ +requires: + - CONFIG_SOC_SDMMC_HOST_SUPPORTED=y + - CONFIG_TINYUSB_MSC_ENABLED=y diff --git a/libraries/SD_MMC/examples/SDMMC_Test/ci.json b/libraries/SD_MMC/examples/SDMMC_Test/ci.json deleted file mode 100644 index a5221dae538..00000000000 --- a/libraries/SD_MMC/examples/SDMMC_Test/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_SDMMC_HOST_SUPPORTED=y" - ] -} diff --git a/libraries/SD_MMC/examples/SDMMC_Test/ci.yml b/libraries/SD_MMC/examples/SDMMC_Test/ci.yml new file mode 100644 index 00000000000..f519ee565aa --- /dev/null +++ b/libraries/SD_MMC/examples/SDMMC_Test/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_SDMMC_HOST_SUPPORTED=y diff --git a/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino b/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino index d1e933e4f4b..ef52089dd38 100644 --- a/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino +++ b/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino @@ -83,10 +83,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -97,10 +98,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SD_MMC/examples/SDMMC_time/ci.json b/libraries/SD_MMC/examples/SDMMC_time/ci.json deleted file mode 100644 index 5552b63d32a..00000000000 --- a/libraries/SD_MMC/examples/SDMMC_time/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_SDMMC_HOST_SUPPORTED=y" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/SD_MMC/examples/SDMMC_time/ci.yml b/libraries/SD_MMC/examples/SDMMC_time/ci.yml new file mode 100644 index 00000000000..a6c887da173 --- /dev/null +++ b/libraries/SD_MMC/examples/SDMMC_time/ci.yml @@ -0,0 +1,6 @@ +requires: + - CONFIG_SOC_SDMMC_HOST_SUPPORTED=y + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/SD_MMC/library.properties b/libraries/SD_MMC/library.properties index f96ee4377c2..6011ed115ea 100644 --- a/libraries/SD_MMC/library.properties +++ b/libraries/SD_MMC/library.properties @@ -1,5 +1,5 @@ name=SD_MMC -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SDMMC File System diff --git a/libraries/SD_MMC/src/SD_MMC.cpp b/libraries/SD_MMC/src/SD_MMC.cpp index 4665198c4ae..12ab2b565eb 100644 --- a/libraries/SD_MMC/src/SD_MMC.cpp +++ b/libraries/SD_MMC/src/SD_MMC.cpp @@ -228,7 +228,19 @@ bool SDMMCFS::begin(const char *mountpoint, bool mode1bit, bool format_if_mount_ #if defined(CONFIG_IDF_TARGET_ESP32P4) && defined(BOARD_SDMMC_SLOT) && (BOARD_SDMMC_SLOT == 0) host.slot = SDMMC_HOST_SLOT_0; // reconfigure slot_config to remove all pins in order to use IO_MUX - slot_config = { + // Use 0 instead of GPIO_NUM_NC (-1) because ESP-IDF's s_check_pin_not_set() + // function uses !pin which doesn't work correctly with -1 (GPIO_NUM_NC) + slot_config = sdmmc_slot_config_t{ + .clk = GPIO_NUM_0, + .cmd = GPIO_NUM_0, + .d0 = GPIO_NUM_0, + .d1 = GPIO_NUM_0, + .d2 = GPIO_NUM_0, + .d3 = GPIO_NUM_0, + .d4 = GPIO_NUM_0, + .d5 = GPIO_NUM_0, + .d6 = GPIO_NUM_0, + .d7 = GPIO_NUM_0, .cd = SDMMC_SLOT_NO_CD, .wp = SDMMC_SLOT_NO_WP, .width = 4, diff --git a/libraries/SD_MMC/src/SD_MMC.h b/libraries/SD_MMC/src/SD_MMC.h index b6fe13a0d24..7192d466b89 100644 --- a/libraries/SD_MMC/src/SD_MMC.h +++ b/libraries/SD_MMC/src/SD_MMC.h @@ -77,7 +77,9 @@ class SDMMCFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD_MMC) extern fs::SDMMCFS SD_MMC; +#endif #endif /* SOC_SDMMC_HOST_SUPPORTED */ #endif /* _SDMMC_H_ */ diff --git a/libraries/SPI/examples/SPI_Multiple_Buses/ci.json b/libraries/SPI/examples/SPI_Multiple_Buses/ci.json deleted file mode 100644 index cd27a02724b..00000000000 --- a/libraries/SPI/examples/SPI_Multiple_Buses/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_SPI_PERIPH_NUM=[2-9]" - ] -} diff --git a/libraries/SPI/examples/SPI_Multiple_Buses/ci.yml b/libraries/SPI/examples/SPI_Multiple_Buses/ci.yml new file mode 100644 index 00000000000..91f0a17f1c0 --- /dev/null +++ b/libraries/SPI/examples/SPI_Multiple_Buses/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_SPI_PERIPH_NUM=[2-9] diff --git a/libraries/SPI/library.properties b/libraries/SPI/library.properties index 3403d1c5d4f..15a17d9101c 100644 --- a/libraries/SPI/library.properties +++ b/libraries/SPI/library.properties @@ -1,5 +1,5 @@ name=SPI -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables the communication with devices that use the Serial Peripheral Interface (SPI) Bus. For all Arduino boards, BUT Arduino DUE. diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 6229f887553..673301a8e15 100644 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -79,22 +79,21 @@ bool SPIClass::begin(int8_t sck, int8_t miso, int8_t mosi, int8_t ss) { } if (sck == -1 && miso == -1 && mosi == -1 && ss == -1) { -#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 +#if CONFIG_IDF_TARGET_ESP32 + _sck = (_spi_num == VSPI) ? SCK : 14; + _miso = (_spi_num == VSPI) ? MISO : 12; + _mosi = (_spi_num == VSPI) ? MOSI : 13; + _ss = (_spi_num == VSPI) ? SS : 15; +#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 _sck = (_spi_num == FSPI) ? SCK : -1; _miso = (_spi_num == FSPI) ? MISO : -1; _mosi = (_spi_num == FSPI) ? MOSI : -1; _ss = (_spi_num == FSPI) ? SS : -1; -#elif CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32P4 \ - || CONFIG_IDF_TARGET_ESP32C5 +#else _sck = SCK; _miso = MISO; _mosi = MOSI; _ss = SS; -#else - _sck = (_spi_num == VSPI) ? SCK : 14; - _miso = (_spi_num == VSPI) ? MISO : 12; - _mosi = (_spi_num == VSPI) ? MOSI : 13; - _ss = (_spi_num == VSPI) ? SS : 15; #endif } else { _sck = sck; diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 6c300e53df2..c00b8155bb9 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -98,7 +98,9 @@ class SPIClass { } }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI) extern SPIClass SPI; +#endif #endif /* SOC_GPSPI_SUPPORTED */ #endif /* _SPI_H_INCLUDED */ diff --git a/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino b/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino index 78a66a94e75..79d8101ae66 100644 --- a/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino +++ b/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino @@ -28,10 +28,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -42,10 +43,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SPIFFS/examples/SPIFFS_time/ci.json b/libraries/SPIFFS/examples/SPIFFS_time/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/SPIFFS/examples/SPIFFS_time/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/SPIFFS/examples/SPIFFS_time/ci.yml b/libraries/SPIFFS/examples/SPIFFS_time/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/SPIFFS/examples/SPIFFS_time/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/SPIFFS/library.properties b/libraries/SPIFFS/library.properties index 486ec1b4ce6..923172dd6ae 100644 --- a/libraries/SPIFFS/library.properties +++ b/libraries/SPIFFS/library.properties @@ -1,5 +1,5 @@ name=SPIFFS -version=3.3.0 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 SPIFFS File System diff --git a/libraries/SPIFFS/src/SPIFFS.cpp b/libraries/SPIFFS/src/SPIFFS.cpp index 0b2cc0d462d..9bb8ff15664 100644 --- a/libraries/SPIFFS/src/SPIFFS.cpp +++ b/libraries/SPIFFS/src/SPIFFS.cpp @@ -91,11 +91,13 @@ void SPIFFSFS::end() { } bool SPIFFSFS::format() { + esp_log_level_set("*", ESP_LOG_NONE); bool wdt_active = disableCore0WDT(); esp_err_t err = esp_spiffs_format(partitionLabel_); if (wdt_active) { enableCore0WDT(); } + esp_log_level_set("*", (esp_log_level_t)CONFIG_LOG_DEFAULT_LEVEL); if (err) { log_e("Formatting SPIFFS failed! Error: %d", err); return false; diff --git a/libraries/SPIFFS/src/SPIFFS.h b/libraries/SPIFFS/src/SPIFFS.h index 2e7bf6de7d8..aad4743bb19 100644 --- a/libraries/SPIFFS/src/SPIFFS.h +++ b/libraries/SPIFFS/src/SPIFFS.h @@ -34,6 +34,8 @@ class SPIFFSFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS) extern fs::SPIFFSFS SPIFFS; +#endif #endif diff --git a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino index a92e576196d..69536bed7e2 100644 --- a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino +++ b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino @@ -18,10 +18,6 @@ #include "SimpleBLE.h" -#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it -#endif - SimpleBLE ble; void onButton() { @@ -34,7 +30,7 @@ void onButton() { void setup() { Serial.begin(115200); Serial.setDebugOutput(true); - pinMode(0, INPUT_PULLUP); + pinMode(BOOT_PIN, INPUT_PULLUP); Serial.print("ESP32 SDK: "); Serial.println(ESP.getSdkVersion()); ble.begin("ESP32 SimpleBLE"); @@ -43,7 +39,7 @@ void setup() { void loop() { static uint8_t lastPinState = 1; - uint8_t pinState = digitalRead(0); + uint8_t pinState = digitalRead(BOOT_PIN); if (!pinState && lastPinState) { onButton(); } diff --git a/libraries/SimpleBLE/examples/SimpleBleDevice/ci.json b/libraries/SimpleBLE/examples/SimpleBleDevice/ci.json deleted file mode 100644 index 3b6a150b31a..00000000000 --- a/libraries/SimpleBLE/examples/SimpleBleDevice/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y", - "CONFIG_BT_ENABLED=y", - "CONFIG_BLUEDROID_ENABLED=y" - ] -} diff --git a/libraries/SimpleBLE/examples/SimpleBleDevice/ci.yml b/libraries/SimpleBLE/examples/SimpleBleDevice/ci.yml new file mode 100644 index 00000000000..87d5a470cd1 --- /dev/null +++ b/libraries/SimpleBLE/examples/SimpleBleDevice/ci.yml @@ -0,0 +1,4 @@ +requires: + - CONFIG_SOC_BLE_SUPPORTED=y + - CONFIG_BT_ENABLED=y + - CONFIG_BLUEDROID_ENABLED=y diff --git a/libraries/SimpleBLE/library.properties b/libraries/SimpleBLE/library.properties index a7f12207afe..cfa030f860d 100644 --- a/libraries/SimpleBLE/library.properties +++ b/libraries/SimpleBLE/library.properties @@ -1,5 +1,5 @@ name=SimpleBLE -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Provides really simple BLE advertizer with just on and off diff --git a/libraries/SimpleBLE/src/SimpleBLE.cpp b/libraries/SimpleBLE/src/SimpleBLE.cpp index 3f1f2bbd1c1..670d371ca9e 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.cpp +++ b/libraries/SimpleBLE/src/SimpleBLE.cpp @@ -12,20 +12,56 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "SimpleBLE.h" #include "esp32-hal-log.h" +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif + +/*************************************************************************** + * Bluedroid includes * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" +#endif + +/*************************************************************************** + * NimBLE includes * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE +#include +#endif + +// Forward declaration +extern "C" void ble_store_config_init(void); +#endif + +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#include "esp32-hal-hosted.h" +#endif +/*************************************************************************** + * Bluedroid data structures * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static esp_ble_adv_data_t _adv_config = { .set_scan_rsp = false, .include_name = true, @@ -55,14 +91,87 @@ static esp_ble_adv_params_t _adv_params = { .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; +#endif + +/*************************************************************************** + * NimBLE data structures * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static struct ble_hs_adv_fields _nimble_adv_fields; +static struct ble_gap_adv_params _nimble_adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_NON, + .disc_mode = BLE_GAP_DISC_MODE_GEN, + .itvl_min = 512, + .itvl_max = 1024, + .channel_map = 0, + .filter_policy = 0, + .high_duty_cycle = 0, +}; +// Global variables for NimBLE synchronization +static bool _nimble_synced = false; +#endif + +// Global state tracking +static bool _ble_initialized = false; + +/*************************************************************************** + * Bluedroid callbacks * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static void _on_gap(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { if (event == ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT) { esp_ble_gap_start_advertising(&_adv_params); } } +#endif + +/*************************************************************************** + * NimBLE callbacks * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static void _nimble_host_task(void *param) { + // This function will be called to run the BLE host + nimble_port_run(); + // Should never reach here unless nimble_port_stop() is called + nimble_port_freertos_deinit(); +} + +static void _nimble_on_reset(int reason) { + log_i("NimBLE reset; reason=%d", reason); +} + +static void _nimble_on_sync(void) { + log_i("NimBLE sync complete"); + _nimble_synced = true; +} + +static int _nimble_gap_event(struct ble_gap_event *event, void *arg) { + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: log_d("BLE_GAP_EVENT_ADV_COMPLETE"); break; + default: break; + } + return 0; +} +#endif +/*************************************************************************** + * Forward declarations * + ***************************************************************************/ +static bool _init_gap(const char *name); +static bool _stop_gap(); +static bool _update_advertising(const char *name); + +/*************************************************************************** + * Initialization functions * + ***************************************************************************/ static bool _init_gap(const char *name) { + if (_ble_initialized) { + log_d("BLE already initialized, skipping"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (!btStarted() && !btStart()) { log_e("btStart failed"); return false; @@ -92,16 +201,178 @@ static bool _init_gap(const char *name) { log_e("gap_register_callback failed"); return false; } + _ble_initialized = true; return true; +#elif defined(CONFIG_NIMBLE_ENABLED) +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + // Initialize esp-hosted transport for BLE HCI when explicitly enabled + if (!hostedInitBLE()) { + log_e("Failed to initialize ESP-Hosted for BLE"); + return false; + } +#endif + + esp_err_t errRc = nimble_port_init(); + if (errRc != ESP_OK) { + log_e("nimble_port_init: rc=%d", errRc); + return false; + } + + // Configure NimBLE host + ble_hs_cfg.reset_cb = _nimble_on_reset; + ble_hs_cfg.sync_cb = _nimble_on_sync; + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + + // Set device name + errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Configure advertising data + memset(&_nimble_adv_fields, 0, sizeof(_nimble_adv_fields)); + _nimble_adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + _nimble_adv_fields.tx_pwr_lvl_is_present = 1; + + // Initialize store configuration + ble_store_config_init(); + + // Start the host task + nimble_port_freertos_init(_nimble_host_task); + + // Wait for sync + int sync_timeout = 1000; // 10 seconds timeout + while (!_nimble_synced && sync_timeout > 0) { + vTaskDelay(pdMS_TO_TICKS(10)); + sync_timeout--; + } + + if (!_nimble_synced) { + log_e("NimBLE sync timeout"); + return false; + } + + // Set advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Start advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + _ble_initialized = true; + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } static bool _stop_gap() { + if (!_ble_initialized) { + log_d("BLE not initialized, nothing to stop"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (btStarted()) { esp_bluedroid_disable(); esp_bluedroid_deinit(); btStop(); } + _ble_initialized = false; + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop advertising + ble_gap_adv_stop(); + + // Stop NimBLE + int rc = nimble_port_stop(); + if (rc != ESP_OK) { + log_e("nimble_port_stop: rc=%d", rc); + } + + nimble_port_deinit(); + _nimble_synced = false; + _ble_initialized = false; + return true; +#else + return true; +#endif +} + +static bool _update_advertising(const char *name) { + if (!_ble_initialized) { + log_e("BLE not initialized"); + return false; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) + // Stop current advertising + esp_ble_gap_stop_advertising(); + + // Set new device name + if (esp_ble_gap_set_device_name(name)) { + log_e("gap_set_device_name failed"); + return false; + } + + // Restart advertising with new name + if (esp_ble_gap_config_adv_data(&_adv_config)) { + log_e("gap_config_adv_data failed"); + return false; + } + + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop current advertising + ble_gap_adv_stop(); + + // Set new device name + int errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Update advertising fields with new name + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + + // Set new advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Restart advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } /* @@ -121,6 +392,12 @@ bool SimpleBLE::begin(String localName) { if (localName.length()) { local_name = localName; } + + // If already initialized, just update advertising data + if (_ble_initialized) { + return _update_advertising(local_name.c_str()); + } + return _init_gap(local_name.c_str()); } @@ -128,4 +405,5 @@ void SimpleBLE::end() { _stop_gap(); } -#endif +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE diff --git a/libraries/SimpleBLE/src/SimpleBLE.h b/libraries/SimpleBLE/src/SimpleBLE.h index df1ee751f10..41a06b0b1aa 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.h +++ b/libraries/SimpleBLE/src/SimpleBLE.h @@ -15,10 +15,10 @@ #ifndef _SIMPLE_BLE_H_ #define _SIMPLE_BLE_H_ -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include #include @@ -26,7 +26,10 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" + +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif #include "Arduino.h" @@ -60,6 +63,7 @@ class SimpleBLE { private: }; -#endif +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED -#endif +#endif // _SIMPLE_BLE_H_ diff --git a/libraries/TFLiteMicro/library.properties b/libraries/TFLiteMicro/library.properties index d2dc127f5ab..52641b149d1 100644 --- a/libraries/TFLiteMicro/library.properties +++ b/libraries/TFLiteMicro/library.properties @@ -1,5 +1,5 @@ name=TFLite Micro -version=3.3.0 +version=3.3.4 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=TensorFlow Lite for Microcontrollers diff --git a/libraries/Ticker/library.properties b/libraries/Ticker/library.properties index 8795deb22ce..441519edab0 100644 --- a/libraries/Ticker/library.properties +++ b/libraries/Ticker/library.properties @@ -1,5 +1,5 @@ name=Ticker -version=3.3.0 +version=3.3.4 author=Bert Melis maintainer=Hristo Gochkov sentence=Allows to call functions with a given interval. diff --git a/libraries/USB/examples/CompositeDevice/ci.json b/libraries/USB/examples/CompositeDevice/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/CompositeDevice/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/CompositeDevice/ci.yml b/libraries/USB/examples/CompositeDevice/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/CompositeDevice/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/ConsumerControl/ci.json b/libraries/USB/examples/ConsumerControl/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/ConsumerControl/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/ConsumerControl/ci.yml b/libraries/USB/examples/ConsumerControl/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/ConsumerControl/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/CustomHIDDevice/ci.json b/libraries/USB/examples/CustomHIDDevice/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/CustomHIDDevice/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/CustomHIDDevice/ci.yml b/libraries/USB/examples/CustomHIDDevice/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/CustomHIDDevice/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/FirmwareMSC/ci.json b/libraries/USB/examples/FirmwareMSC/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/FirmwareMSC/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/FirmwareMSC/ci.yml b/libraries/USB/examples/FirmwareMSC/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/FirmwareMSC/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Gamepad/ci.json b/libraries/USB/examples/Gamepad/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Gamepad/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Gamepad/ci.yml b/libraries/USB/examples/Gamepad/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Gamepad/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/HIDVendor/ci.json b/libraries/USB/examples/HIDVendor/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/HIDVendor/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/HIDVendor/ci.yml b/libraries/USB/examples/HIDVendor/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/HIDVendor/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/ci.json b/libraries/USB/examples/Keyboard/KeyboardLogout/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Keyboard/KeyboardLogout/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Keyboard/KeyboardLogout/ci.yml b/libraries/USB/examples/Keyboard/KeyboardLogout/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardLogout/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/ci.json b/libraries/USB/examples/Keyboard/KeyboardMessage/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Keyboard/KeyboardMessage/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Keyboard/KeyboardMessage/ci.yml b/libraries/USB/examples/Keyboard/KeyboardMessage/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardMessage/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.json b/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.yml b/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardReprogram/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/ci.json b/libraries/USB/examples/Keyboard/KeyboardSerial/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Keyboard/KeyboardSerial/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Keyboard/KeyboardSerial/ci.yml b/libraries/USB/examples/Keyboard/KeyboardSerial/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Keyboard/KeyboardSerial/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/KeyboardAndMouseControl/ci.json b/libraries/USB/examples/KeyboardAndMouseControl/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/KeyboardAndMouseControl/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/KeyboardAndMouseControl/ci.yml b/libraries/USB/examples/KeyboardAndMouseControl/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/KeyboardAndMouseControl/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/MIDI/MidiController/MidiController.ino b/libraries/USB/examples/MIDI/MidiController/MidiController.ino index 2871d3b1a52..9cdd8a805de 100644 --- a/libraries/USB/examples/MIDI/MidiController/MidiController.ino +++ b/libraries/USB/examples/MIDI/MidiController/MidiController.ino @@ -20,7 +20,8 @@ void loop() {} #include "USB.h" #include "USBMIDI.h" -USBMIDI MIDI; +// Creates the MIDI device with specific descriptor +USBMIDI MIDI("ESP MIDI Device"); #define MIDI_NOTE_C4 60 diff --git a/libraries/USB/examples/MIDI/MidiController/ci.json b/libraries/USB/examples/MIDI/MidiController/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/MIDI/MidiController/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/MIDI/MidiController/ci.yml b/libraries/USB/examples/MIDI/MidiController/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiController/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino index e3ad1c4e028..e118d5ddb9a 100644 --- a/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino +++ b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino @@ -24,8 +24,12 @@ void setup() {} void loop() {} #else +// define a new USB MIDI device name using a macro +SET_USB_MIDI_DEVICE_NAME("ESP MIDI Device") + #include "USB.h" #include "USBMIDI.h" +// Creates the MIDI device with specific name defined with the SET_USB_MIDI_DEVICE_NAME() macro USBMIDI MIDI; #define MIDI_RX 39 diff --git a/libraries/USB/examples/MIDI/MidiInterface/ci.json b/libraries/USB/examples/MIDI/MidiInterface/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/MIDI/MidiInterface/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/MIDI/MidiInterface/ci.yml b/libraries/USB/examples/MIDI/MidiInterface/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiInterface/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/ci.json b/libraries/USB/examples/MIDI/MidiMusicBox/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/MIDI/MidiMusicBox/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/MIDI/MidiMusicBox/ci.yml b/libraries/USB/examples/MIDI/MidiMusicBox/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/MIDI/MidiMusicBox/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/ci.json b/libraries/USB/examples/MIDI/ReceiveMidi/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/MIDI/ReceiveMidi/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/MIDI/ReceiveMidi/ci.yml b/libraries/USB/examples/MIDI/ReceiveMidi/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/MIDI/ReceiveMidi/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/ci.json b/libraries/USB/examples/Mouse/ButtonMouseControl/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/Mouse/ButtonMouseControl/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/Mouse/ButtonMouseControl/ci.yml b/libraries/USB/examples/Mouse/ButtonMouseControl/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/Mouse/ButtonMouseControl/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/SystemControl/ci.json b/libraries/USB/examples/SystemControl/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/SystemControl/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/SystemControl/ci.yml b/libraries/USB/examples/SystemControl/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/SystemControl/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/USBMSC/ci.json b/libraries/USB/examples/USBMSC/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/USBMSC/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/USBMSC/ci.yml b/libraries/USB/examples/USBMSC/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/USBMSC/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/USBSerial/ci.json b/libraries/USB/examples/USBSerial/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/USBSerial/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/USBSerial/ci.yml b/libraries/USB/examples/USBSerial/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/USBSerial/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/examples/USBVendor/ci.json b/libraries/USB/examples/USBVendor/ci.json deleted file mode 100644 index f9ac7d0bec9..00000000000 --- a/libraries/USB/examples/USBVendor/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_USB_OTG_SUPPORTED=y" - ] -} diff --git a/libraries/USB/examples/USBVendor/ci.yml b/libraries/USB/examples/USBVendor/ci.yml new file mode 100644 index 00000000000..047516fdb45 --- /dev/null +++ b/libraries/USB/examples/USBVendor/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_USB_OTG_SUPPORTED=y diff --git a/libraries/USB/library.properties b/libraries/USB/library.properties index 4c2c032545e..8698f2cffbe 100644 --- a/libraries/USB/library.properties +++ b/libraries/USB/library.properties @@ -1,5 +1,5 @@ name=USB -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32S2 USB Library diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index 8a9571855e1..61b75f0144d 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -6,6 +6,13 @@ #include "Arduino.h" #include "esp32-hal-tinyusb.h" +// Initialize static members +char *USBMIDI::midiUserDeviceName = nullptr; +// Weak definition of getUSBMIDIDefaultDeviceName to provide a default name +__attribute__((weak)) const char *getUSBMIDIDefaultDeviceName() { + return ESP32_USB_MIDI_DEFAULT_NAME; +} + // Default Cable Number (for simplified APIs that do not expose this) #define DEFAULT_CN 0 @@ -18,7 +25,7 @@ extern "C" uint16_t tusb_midi_load_descriptor(uint8_t *dst, uint8_t *itf) { } tinyusb_midi_descriptor_loaded = true; - uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MIDI"); + uint8_t str_index = tinyusb_add_string_descriptor(USBMIDI::getCurrentDeviceName()); uint8_t ep_in = tinyusb_get_free_in_endpoint(); TU_VERIFY(ep_in != 0); uint8_t ep_out = tinyusb_get_free_out_endpoint(); @@ -41,9 +48,68 @@ USBMIDI::USBMIDI() { } } +// private function for setting a not null/empty MIDI device name limited to 32 characters +void USBMIDI::setDeviceName(const char *name) { + const uint8_t maxNameLength = 32; // tinyUSB Descriptor limit + if (name != nullptr && strlen(name) > 0) { + if (strlen(name) > maxNameLength) { + log_w("USBMIDI: Device name too long, truncating to %d characters.", maxNameLength); + } + if (!midiUserDeviceName) { + midiUserDeviceName = new char[maxNameLength + 1]; // +1 for null-terminator + } + if (midiUserDeviceName) { + strncpy(midiUserDeviceName, name, maxNameLength); + // Ensure null-termination when overflowing + midiUserDeviceName[maxNameLength] = '\0'; + } else { + log_e("USBMIDI: Failed to allocate memory for device name, using default name."); + } + } else { + log_w("USBMIDI: No device name provided, using default name [%s].", getUSBMIDIDefaultDeviceName()); + } +} + +/** +* @brief Constructor for setting the current device name +* 1. Name set via constructor (if any) +* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) +* 3. Default name "TinyUSB MIDI" +* If device name is set as "", it will be ignored +*/ +USBMIDI::USBMIDI(const char *name) { + if (!tinyusb_midi_interface_enabled) { + setDeviceName(name); + tinyusb_midi_interface_enabled = true; + tinyusb_enable_interface(USB_INTERFACE_MIDI, TUD_MIDI_DESC_LEN, tusb_midi_load_descriptor); + } else { + log_e("USBMIDI: Multiple instances of USBMIDI not supported!"); + } +} + +USBMIDI::~USBMIDI() { + if (midiUserDeviceName) { + delete[] midiUserDeviceName; + midiUserDeviceName = nullptr; + } +} + void USBMIDI::begin() {} void USBMIDI::end() {} +const char *USBMIDI::getCurrentDeviceName(void) { + if (midiUserDeviceName) { + return midiUserDeviceName; + } + // If no user name set, use the compile-time default name limited to 32 characters + setDeviceName(getUSBMIDIDefaultDeviceName()); + if (midiUserDeviceName && strlen(midiUserDeviceName)) { + return midiUserDeviceName; + } else { + return "TinyUSB MIDI"; + } +} + // uint compatible version of constrain #define uconstrain(amt, low, high) ((amt) <= (low) ? (low) : ((amt) > (high) ? (high) : (amt))) @@ -92,7 +158,7 @@ void USBMIDI::channelPressure(uint8_t pressure, uint8_t channel) { // Pitch Bend Change [-8192,0,8191] void USBMIDI::pitchBend(int16_t value, uint8_t channel) { uint16_t pitchBendValue = constrain(value, -8192, 8191) + 8192; - pitchBend(pitchBendValue); + pitchBend(pitchBendValue, channel); } // Pitch Bend Change [0,8192,16383] diff --git a/libraries/USB/src/USBMIDI.h b/libraries/USB/src/USBMIDI.h index 91a1bfa4be1..8f111dbfd4a 100644 --- a/libraries/USB/src/USBMIDI.h +++ b/libraries/USB/src/USBMIDI.h @@ -18,11 +18,41 @@ typedef struct { } midiEventPacket_t; class USBMIDI { +private: + static char *midiUserDeviceName; // user device name + static void setDeviceName(const char *name); // set user device name limited to 32 characters + public: + /** + * @brief Default constructor + * Will use the compile-time name if set via SET_USB_MIDI_DEVICE_NAME(), + * otherwise uses "TinyUSB MIDI" + */ USBMIDI(void); + + /** + * @brief Set the current device name + * 1. Name set via constructor (if any) + * 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) + * 3. Default name "TinyUSB MIDI" + * It has no effect if name is set as NULL or "" + */ + USBMIDI(const char *name); + + ~USBMIDI(); + void begin(void); void end(void); + /** + * @brief Get the current device name + * @return The device name in order of precedence: + * 1. Name set via constructor (if any) + * 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) + * 3. Default name "TinyUSB MIDI" + */ + static const char *getCurrentDeviceName(void); + /* User-level API */ // Note On diff --git a/libraries/Update/examples/AWS_S3_OTA_Update/ci.json b/libraries/Update/examples/AWS_S3_OTA_Update/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Update/examples/AWS_S3_OTA_Update/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Update/examples/AWS_S3_OTA_Update/ci.yml b/libraries/Update/examples/AWS_S3_OTA_Update/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/AWS_S3_OTA_Update/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Update/examples/HTTPS_OTA_Update/ci.json b/libraries/Update/examples/HTTPS_OTA_Update/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Update/examples/HTTPS_OTA_Update/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Update/examples/HTTPS_OTA_Update/ci.yml b/libraries/Update/examples/HTTPS_OTA_Update/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/HTTPS_OTA_Update/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.json b/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.yml b/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/HTTP_Client_AES_OTA_Update/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Update/examples/HTTP_Server_AES_OTA_Update/HTTP_Server_AES_OTA_Update.ino b/libraries/Update/examples/HTTP_Server_AES_OTA_Update/HTTP_Server_AES_OTA_Update.ino index 5af4f1bf9f4..f9216f678a5 100644 --- a/libraries/Update/examples/HTTP_Server_AES_OTA_Update/HTTP_Server_AES_OTA_Update.ino +++ b/libraries/Update/examples/HTTP_Server_AES_OTA_Update/HTTP_Server_AES_OTA_Update.ino @@ -18,7 +18,7 @@ defaults:- {if not set ie. "Update.setupCrypt();" } OTA_MODE options:- U_AES_DECRYPT_NONE decryption disabled, loads OTA image files as sent(plain) - U_AES_DECRYPT_AUTO auto loads both plain & encrypted OTA FLASH image files, and plain OTA SPIFFS image files + U_AES_DECRYPT_AUTO auto loads both plain & encrypted OTA FLASH image files, and plain OTA File System image files U_AES_DECRYPT_ON decrypts OTA image files https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/ @@ -36,7 +36,6 @@ espsecure.py encrypt_flash_data = runs the idf encryption function to make a en #include #include -#include #include #include #include @@ -145,7 +144,7 @@ void setupHttpUpdateServer() { if (upload.status == UPLOAD_FILE_START) { Serial.printf("Update: %s\n", upload.filename.c_str()); if (upload.name == "filesystem") { - if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) { //start with max available size + if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_FLASHFS)) { //start with max available size Update.printError(Serial); } } else { diff --git a/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.json b/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.yml b/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/HTTP_Server_AES_OTA_Update/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Update/examples/OTAWebUpdater/ci.json b/libraries/Update/examples/OTAWebUpdater/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/Update/examples/OTAWebUpdater/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/Update/examples/OTAWebUpdater/ci.yml b/libraries/Update/examples/OTAWebUpdater/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/OTAWebUpdater/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/Update/library.properties b/libraries/Update/library.properties index 5fd633ec358..e1b77c462b1 100644 --- a/libraries/Update/library.properties +++ b/libraries/Update/library.properties @@ -1,5 +1,5 @@ name=Update -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32 Sketch Update Library diff --git a/libraries/Update/src/HttpsOTAUpdate.h b/libraries/Update/src/HttpsOTAUpdate.h index d470ad50722..8eadc799db5 100644 --- a/libraries/Update/src/HttpsOTAUpdate.h +++ b/libraries/Update/src/HttpsOTAUpdate.h @@ -27,5 +27,8 @@ class HttpsOTAUpdateClass { HttpsOTAStatus_t status(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPSOTAUPDATE) extern HttpsOTAUpdateClass HttpsOTA; #endif + +#endif diff --git a/libraries/Update/src/Update.h b/libraries/Update/src/Update.h index 9a4d3e02489..5f52e6b3b73 100644 --- a/libraries/Update/src/Update.h +++ b/libraries/Update/src/Update.h @@ -29,9 +29,12 @@ #define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF -#define U_FLASH 0 -#define U_SPIFFS 100 -#define U_AUTH 200 +#define U_FLASH 0 +#define U_FLASHFS 100 +#define U_SPIFFS 101 +#define U_FATFS 102 +#define U_LITTLEFS 103 +#define U_AUTH 200 #define ENCRYPTED_BLOCK_SIZE 16 #define ENCRYPTED_TWEAK_BLOCK_SIZE 32 @@ -267,7 +270,6 @@ class UpdateClass { size_t _size; THandlerFunction_Progress _progress_callback; uint32_t _progress; - uint32_t _paroffset; uint32_t _command; const esp_partition_t *_partition; diff --git a/libraries/Update/src/Updater.cpp b/libraries/Update/src/Updater.cpp index 3b0c517431d..3d3569f019e 100644 --- a/libraries/Update/src/Updater.cpp +++ b/libraries/Update/src/Updater.cpp @@ -75,7 +75,7 @@ UpdateClass::UpdateClass() #ifndef UPDATE_NOCRYPT _cryptKey(0), _cryptBuffer(0), #endif /* UPDATE_NOCRYPT */ - _buffer(0), _skipBuffer(0), _bufferLen(0), _size(0), _progress_callback(NULL), _progress(0), _paroffset(0), _command(U_FLASH), _partition(NULL) + _buffer(0), _skipBuffer(0), _bufferLen(0), _size(0), _progress_callback(NULL), _progress(0), _command(U_FLASH), _partition(NULL) #ifndef UPDATE_NOCRYPT , _cryptMode(U_AES_DECRYPT_AUTO), _cryptAddress(0), _cryptCfg(0xf) @@ -128,6 +128,8 @@ bool UpdateClass::rollBack() { } bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, const char *label) { + (void)label; + if (_size > 0) { log_w("already running"); return false; @@ -154,16 +156,39 @@ bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, con } log_d("OTA Partition: %s", _partition->label); } else if (command == U_SPIFFS) { - _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, label); - _paroffset = 0; + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); + if (!_partition) { + _error = UPDATE_ERROR_NO_PARTITION; + return false; + } + log_d("SPIFFS Partition: %s", _partition->label); + } else if (command == U_FATFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + if (!_partition) { + _error = UPDATE_ERROR_NO_PARTITION; + return false; + } + log_d("FATFS Partition: %s", _partition->label); + } else if (command == U_LITTLEFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, NULL); + if (!_partition) { + _error = UPDATE_ERROR_NO_PARTITION; + return false; + } + log_d("LittleFS Partition: %s", _partition->label); + } else if (command == U_FLASHFS) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, NULL); if (!_partition) { _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); - _paroffset = 0x1000; //Offset for ffat, assuming size is already corrected - if (!_partition) { - _error = UPDATE_ERROR_NO_PARTITION; - return false; - } } + if (!_partition) { + _partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, NULL); + } + if (!_partition) { + _error = UPDATE_ERROR_NO_PARTITION; + return false; + } + log_d("FS Partition: %s", _partition->label); } else { _error = UPDATE_ERROR_BAD_ARGUMENT; log_e("bad command %u", command); @@ -452,7 +477,7 @@ bool UpdateClass::_verifyHeader(uint8_t data) { return false; } return true; - } else if (_command == U_SPIFFS) { + } else { return true; } return false; @@ -471,7 +496,7 @@ bool UpdateClass::_verifyEnd() { } _reset(); return true; - } else if (_command == U_SPIFFS) { + } else { _reset(); return true; } diff --git a/libraries/WebServer/examples/AdvancedWebServer/ci.json b/libraries/WebServer/examples/AdvancedWebServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/AdvancedWebServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/AdvancedWebServer/ci.yml b/libraries/WebServer/examples/AdvancedWebServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/AdvancedWebServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/ChunkWriting/ChunkWriting.ino b/libraries/WebServer/examples/ChunkWriting/ChunkWriting.ino new file mode 100644 index 00000000000..50c29322302 --- /dev/null +++ b/libraries/WebServer/examples/ChunkWriting/ChunkWriting.ino @@ -0,0 +1,75 @@ +/* + * This example demonstrates how to send an HTTP response using chunks + * It will create an HTTP Server (port 80) associated with an a MDNS service + * Access the HTTP server using a Web Browser: + * URL can be composed using the MDNS name "esp32_chunk_resp.local" + * http://esp32_chunk_resp.local/ + * or the IP Address that will be printed out, such as for instance 192.168.1.10 + * http://192.168.1.10/ + * + * ESP32 Server response can also be viewed using the curl command: + * curl -i esp32_chunk_resp.local:80 + * curl -i --raw esp32_chunk_resp.local:80 + */ + +#include +#include +#include +#include + +const char *ssid = "........"; +const char *password = "........"; + +WebServer server(80); + +void handleChunks() { + uint8_t countDown = 10; + server.chunkResponseBegin(); + char countContent[8]; + while (countDown) { + sprintf(countContent, "%d...\r\n", countDown--); + server.chunkWrite(countContent, strlen(countContent)); + // count down shall show up in the browser only after about 5 seconds when finishing the whole transmission + // using "curl -i esp32_chunk_resp.local:80", it will show the count down as it sends each chunk + delay(500); + } + server.chunkWrite("DONE!", 5); + server.chunkResponseEnd(); +} + +void setup(void) { + Serial.begin(115200); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + Serial.println(""); + + // Wait for connection + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + // Use the URL: http://esp32_chunk_resp.local/ + if (MDNS.begin("esp32_chunk_resp")) { + Serial.println("MDNS responder started"); + } + + server.on("/", handleChunks); + + server.onNotFound([]() { + server.send(404, "text/plain", "Page not found"); + }); + + server.begin(); + Serial.println("HTTP server started"); +} + +void loop(void) { + server.handleClient(); + delay(2); //allow the cpu to switch to other tasks +} diff --git a/libraries/WebServer/examples/ChunkWriting/ci.yml b/libraries/WebServer/examples/ChunkWriting/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/ChunkWriting/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/FSBrowser/ci.json b/libraries/WebServer/examples/FSBrowser/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/FSBrowser/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/FSBrowser/ci.yml b/libraries/WebServer/examples/FSBrowser/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/FSBrowser/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/Filters/ci.json b/libraries/WebServer/examples/Filters/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/Filters/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/Filters/ci.yml b/libraries/WebServer/examples/Filters/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/Filters/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HelloServer/ci.json b/libraries/WebServer/examples/HelloServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HelloServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HelloServer/ci.yml b/libraries/WebServer/examples/HelloServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HelloServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpAdvancedAuth/ci.json b/libraries/WebServer/examples/HttpAdvancedAuth/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpAdvancedAuth/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpAdvancedAuth/ci.yml b/libraries/WebServer/examples/HttpAdvancedAuth/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpAdvancedAuth/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpAuthCallback/ci.json b/libraries/WebServer/examples/HttpAuthCallback/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpAuthCallback/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpAuthCallback/ci.yml b/libraries/WebServer/examples/HttpAuthCallback/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpAuthCallback/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpAuthCallbackInline/ci.json b/libraries/WebServer/examples/HttpAuthCallbackInline/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpAuthCallbackInline/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpAuthCallbackInline/ci.yml b/libraries/WebServer/examples/HttpAuthCallbackInline/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpAuthCallbackInline/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpBasicAuth/ci.json b/libraries/WebServer/examples/HttpBasicAuth/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpBasicAuth/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpBasicAuth/ci.yml b/libraries/WebServer/examples/HttpBasicAuth/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpBasicAuth/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.json b/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.yml b/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpBasicAuthSHA1/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.json b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.yml b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/HttpBasicAuthSHA1orBearerToken/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/Middleware/ci.json b/libraries/WebServer/examples/Middleware/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/WebServer/examples/Middleware/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/WebServer/examples/Middleware/ci.yml b/libraries/WebServer/examples/Middleware/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/WebServer/examples/Middleware/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/WebServer/examples/MultiHomedServers/ci.json b/libraries/WebServer/examples/MultiHomedServers/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/MultiHomedServers/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/MultiHomedServers/ci.yml b/libraries/WebServer/examples/MultiHomedServers/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/MultiHomedServers/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/PathArgServer/ci.json b/libraries/WebServer/examples/PathArgServer/ci.json deleted file mode 100644 index cbdd28f773d..00000000000 --- a/libraries/WebServer/examples/PathArgServer/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/PathArgServer/ci.yml b/libraries/WebServer/examples/PathArgServer/ci.yml new file mode 100644 index 00000000000..9f15b3468e6 --- /dev/null +++ b/libraries/WebServer/examples/PathArgServer/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/SDWebServer/ci.json b/libraries/WebServer/examples/SDWebServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/SDWebServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/SDWebServer/ci.yml b/libraries/WebServer/examples/SDWebServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/SDWebServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/SimpleAuthentification/ci.json b/libraries/WebServer/examples/SimpleAuthentification/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/SimpleAuthentification/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/SimpleAuthentification/ci.yml b/libraries/WebServer/examples/SimpleAuthentification/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/SimpleAuthentification/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/UploadHugeFile/ci.json b/libraries/WebServer/examples/UploadHugeFile/ci.json deleted file mode 100644 index cbdd28f773d..00000000000 --- a/libraries/WebServer/examples/UploadHugeFile/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/UploadHugeFile/ci.yml b/libraries/WebServer/examples/UploadHugeFile/ci.yml new file mode 100644 index 00000000000..9f15b3468e6 --- /dev/null +++ b/libraries/WebServer/examples/UploadHugeFile/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=huge_app + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/WebServer/ci.json b/libraries/WebServer/examples/WebServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/WebServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/WebServer/ci.yml b/libraries/WebServer/examples/WebServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/WebServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/examples/WebUpdate/ci.json b/libraries/WebServer/examples/WebUpdate/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/WebUpdate/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WebServer/examples/WebUpdate/ci.yml b/libraries/WebServer/examples/WebUpdate/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WebServer/examples/WebUpdate/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WebServer/library.properties b/libraries/WebServer/library.properties index 913dd00e036..4d491effc14 100644 --- a/libraries/WebServer/library.properties +++ b/libraries/WebServer/library.properties @@ -1,5 +1,5 @@ name=WebServer -version=3.3.0 +version=3.3.4 author=Ivan Grokhotkov maintainer=Ivan Grokhtkov sentence=Simple web server library diff --git a/libraries/WebServer/src/Parsing.cpp b/libraries/WebServer/src/Parsing.cpp index 3030317eeea..df8051022ff 100644 --- a/libraries/WebServer/src/Parsing.cpp +++ b/libraries/WebServer/src/Parsing.cpp @@ -189,8 +189,8 @@ bool WebServer::_parseRequest(NetworkClient &client) { _currentHandler->raw(*this, _currentUri, *_currentRaw); _currentRaw->status = RAW_WRITE; - while (_currentRaw->totalSize < _clientContentLength) { - size_t read_len = std::min(_clientContentLength - _currentRaw->totalSize, (size_t)HTTP_RAW_BUFLEN); + while (_currentRaw->totalSize < (size_t)_clientContentLength) { + size_t read_len = std::min((size_t)_clientContentLength - _currentRaw->totalSize, (size_t)HTTP_RAW_BUFLEN); _currentRaw->currentSize = client.readBytes(_currentRaw->buf, read_len); _currentRaw->totalSize += _currentRaw->currentSize; if (_currentRaw->currentSize == 0) { @@ -206,7 +206,7 @@ bool WebServer::_parseRequest(NetworkClient &client) { } else if (!isForm) { size_t plainLength; char *plainBuf = readBytesWithTimeout(client, _clientContentLength, plainLength, HTTP_MAX_POST_WAIT); - if (plainLength < _clientContentLength) { + if (plainLength < (size_t)_clientContentLength) { free(plainBuf); return false; } @@ -407,7 +407,7 @@ int WebServer::_uploadReadByte(NetworkClient &client) { bool WebServer::_parseForm(NetworkClient &client, const String &boundary, uint32_t len) { (void)len; - log_v("Parse Form: Boundary: %s Length: %d", boundary.c_str(), len); + log_v("Parse Form: Boundary: %s Length: %u", boundary.c_str(), len); String line; int retry = 0; do { @@ -432,7 +432,7 @@ bool WebServer::_parseForm(NetworkClient &client, const String &boundary, uint32 line = client.readStringUntil('\r'); client.readStringUntil('\n'); - if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { + if (line.length() > (size_t)19 && line.substring(0, 19).equalsIgnoreCase(F("Content-Disposition"))) { int nameStart = line.indexOf('='); if (nameStart != -1) { argName = line.substring(nameStart + 2); @@ -455,7 +455,7 @@ bool WebServer::_parseForm(NetworkClient &client, const String &boundary, uint32 line = client.readStringUntil('\r'); client.readStringUntil('\n'); while (line.length() > 0) { - if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { + if (line.length() > (size_t)12 && line.substring(0, 12).equalsIgnoreCase(FPSTR(Content_Type))) { argType = line.substring(line.indexOf(':') + 2); } //skip over any other headers @@ -470,7 +470,7 @@ bool WebServer::_parseForm(NetworkClient &client, const String &boundary, uint32 if (line.startsWith("--" + boundary)) { break; } - if (argValue.length() > 0) { + if (argValue.length() > (size_t)0) { argValue += "\n"; } argValue += line; diff --git a/libraries/WebServer/src/WebServer.cpp b/libraries/WebServer/src/WebServer.cpp index 7523e40259b..98ec929f725 100644 --- a/libraries/WebServer/src/WebServer.cpp +++ b/libraries/WebServer/src/WebServer.cpp @@ -143,6 +143,8 @@ bool WebServer::authenticateBasicSHA1(const char *_username, const char *_sha1Ba bool WebServer::authenticate(const char *_username, const char *_password) { return WebServer::authenticate([_username, _password](HTTPAuthMethod mode, String username, String params[]) -> String * { + (void)mode; + (void)params; return username.equalsConstantTime(_username) ? new String(_password) : NULL; }); } @@ -551,6 +553,76 @@ void WebServer::enableETag(bool enable, ETagFunction fn) { _eTagFunction = fn; } +void WebServer::chunkResponseBegin(const char *contentType) { + if (_chunkedResponseActive) { + log_e("Already in chunked response mode"); + return; + } + + if (strchr(contentType, '\r') || strchr(contentType, '\n')) { + log_e("Invalid character in content type"); + return; + } + + _chunkedResponseActive = true; + _chunkedClient = _currentClient; + + _contentLength = CONTENT_LENGTH_UNKNOWN; + + String header; + _prepareHeader(header, 200, contentType, 0); + _currentClientWrite(header.c_str(), header.length()); + + _chunkedResponseActive = true; + _chunkedClient = _currentClient; +} + +void WebServer::chunkWrite(const char *data, size_t length) { + if (!_chunkedResponseActive) { + log_e("Chunked response has not been started"); + return; + } + + char chunkSize[11]; + snprintf(chunkSize, sizeof(chunkSize), "%zx\r\n", length); + + if (_chunkedClient.write(chunkSize) != strlen(chunkSize)) { + log_e("Failed to write chunk size"); + _chunkedResponseActive = false; + return; + } + + if (_chunkedClient.write((const uint8_t *)data, length) != length) { + log_e("Failed to write chunk data"); + _chunkedResponseActive = false; + return; + } + + if (_chunkedClient.write("\r\n") != 2) { + log_e("Failed to write chunk terminator"); + _chunkedResponseActive = false; + return; + } +} + +void WebServer::chunkResponseEnd() { + if (!_chunkedResponseActive) { + log_e("Chunked response has not been started"); + return; + } + + if (_chunkedClient.write("0\r\n\r\n", 5) != 5) { + log_e("Failed to write terminating chunk"); + } + + _chunkedClient.clear(); + _chunkedResponseActive = false; + _chunked = false; + _chunkedClient = NetworkClient(); + + _clearResponseHeaders(); +} + void WebServer::_prepareHeader(String &response, int code, const char *content_type, size_t contentLength) { _responseCode = code; diff --git a/libraries/WebServer/src/WebServer.h b/libraries/WebServer/src/WebServer.h index 8daf12c5c30..498bcb5806c 100644 --- a/libraries/WebServer/src/WebServer.h +++ b/libraries/WebServer/src/WebServer.h @@ -115,6 +115,10 @@ class WebServer { const String AuthTypeDigest = F("Digest"); const String AuthTypeBasic = F("Basic"); + void chunkResponseBegin(const char *contentType = "text/plain"); + void chunkWrite(const char *data, size_t length); + void chunkResponseEnd(); + /* Callbackhandler for authentication. The extra parameters depend on the * HTTPAuthMethod mode: * @@ -241,6 +245,10 @@ class WebServer { static String responseCodeToString(int code); +private: + bool _chunkedResponseActive = false; + NetworkClient _chunkedClient; // Store by value, no dangling pointer + protected: virtual size_t _currentClientWrite(const char *b, size_t l) { return _currentClient.write(b, l); diff --git a/libraries/WebServer/src/detail/RequestHandlersImpl.h b/libraries/WebServer/src/detail/RequestHandlersImpl.h index 3750b594ab2..b77ebd0c90e 100644 --- a/libraries/WebServer/src/detail/RequestHandlersImpl.h +++ b/libraries/WebServer/src/detail/RequestHandlersImpl.h @@ -36,6 +36,7 @@ RequestHandler &RequestHandler::removeMiddleware(Middleware *middleware) { bool RequestHandler::process(WebServer &server, HTTPMethod requestMethod, String requestUri) { if (_chain) { return _chain->runChain(server, [this, &server, &requestMethod, &requestUri]() { + (void)requestUri; return handle(server, requestMethod, requestUri); }); } else { @@ -71,6 +72,7 @@ class FunctionRequestHandler : public RequestHandler { } bool canRaw(const String &requestUri) override { + (void)requestUri; if (!_ufn || _method == HTTP_GET) { return false; } @@ -95,6 +97,7 @@ class FunctionRequestHandler : public RequestHandler { } bool canRaw(WebServer &server, const String &requestUri) override { + (void)requestUri; if (!_ufn || _method == HTTP_GET || (_filter != NULL ? _filter(server) == false : false)) { return false; } diff --git a/libraries/WebServer/src/detail/mimetable.cpp b/libraries/WebServer/src/detail/mimetable.cpp index 758f3ad34ef..036de07d599 100644 --- a/libraries/WebServer/src/detail/mimetable.cpp +++ b/libraries/WebServer/src/detail/mimetable.cpp @@ -10,6 +10,7 @@ const Entry mimeTable[maxType] = { {".css", "text/css"}, {".txt", "text/plain"}, {".js", "application/javascript"}, + {".mjs", "text/javascript"}, {".json", "application/json"}, {".png", "image/png"}, {".gif", "image/gif"}, diff --git a/libraries/WebServer/src/detail/mimetable.h b/libraries/WebServer/src/detail/mimetable.h index 4732e59c466..869b28ea111 100644 --- a/libraries/WebServer/src/detail/mimetable.h +++ b/libraries/WebServer/src/detail/mimetable.h @@ -9,6 +9,7 @@ enum type { css, txt, js, + mjs, json, png, gif, diff --git a/libraries/WebServer/src/middleware/Middleware.h b/libraries/WebServer/src/middleware/Middleware.h index 080f5be0aba..01d84a02e34 100644 --- a/libraries/WebServer/src/middleware/Middleware.h +++ b/libraries/WebServer/src/middleware/Middleware.h @@ -15,6 +15,7 @@ class Middleware { virtual ~Middleware() {} virtual bool run(WebServer &server, Callback next) { + (void)server; return next(); }; diff --git a/libraries/WiFi/examples/FTM/FTM_Initiator/ci.json b/libraries/WiFi/examples/FTM/FTM_Initiator/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/FTM/FTM_Initiator/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/FTM/FTM_Initiator/ci.yml b/libraries/WiFi/examples/FTM/FTM_Initiator/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/FTM/FTM_Initiator/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/FTM/FTM_Responder/ci.json b/libraries/WiFi/examples/FTM/FTM_Responder/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/FTM/FTM_Responder/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/FTM/FTM_Responder/ci.yml b/libraries/WiFi/examples/FTM/FTM_Responder/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/FTM/FTM_Responder/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/SimpleWiFiServer/ci.json b/libraries/WiFi/examples/SimpleWiFiServer/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/SimpleWiFiServer/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/SimpleWiFiServer/ci.yml b/libraries/WiFi/examples/SimpleWiFiServer/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/SimpleWiFiServer/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WPS/ci.json b/libraries/WiFi/examples/WPS/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/WiFi/examples/WPS/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/WiFi/examples/WPS/ci.yml b/libraries/WiFi/examples/WPS/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/WiFi/examples/WPS/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/WiFi/examples/WiFiAccessPoint/ci.json b/libraries/WiFi/examples/WiFiAccessPoint/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiAccessPoint/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiAccessPoint/ci.yml b/libraries/WiFi/examples/WiFiAccessPoint/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiAccessPoint/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino index 805458ce694..942846ff1a4 100644 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino +++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino @@ -15,6 +15,11 @@ // Sketch shows how to switch between WiFi and BlueTooth or use both // Button is attached between GPIO 0 and GND and modes are switched with each press +#include "soc/soc_caps.h" +#if !CONFIG_SOC_BT_SUPPORTED +#error "This example requires native Bluetooth support" +#endif + #include "WiFi.h" #define STA_SSID "your-ssid" #define STA_PASS "your-pass" diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json deleted file mode 100644 index f27dd13c83e..00000000000 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "requires": [ - "CONFIG_BT_ENABLED=y" - ], - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.yml b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.yml new file mode 100644 index 00000000000..62aa0cb6119 --- /dev/null +++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.yml @@ -0,0 +1,6 @@ +requires: + - CONFIG_SOC_BT_SUPPORTED=y + +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiClient/ci.json b/libraries/WiFi/examples/WiFiClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClient/ci.yml b/libraries/WiFi/examples/WiFiClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiClientBasic/ci.json b/libraries/WiFi/examples/WiFiClientBasic/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiClientBasic/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClientBasic/ci.yml b/libraries/WiFi/examples/WiFiClientBasic/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiClientBasic/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiClientConnect/ci.json b/libraries/WiFi/examples/WiFiClientConnect/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiClientConnect/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClientConnect/ci.yml b/libraries/WiFi/examples/WiFiClientConnect/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiClientConnect/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiClientEnterprise/ci.json b/libraries/WiFi/examples/WiFiClientEnterprise/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/WiFi/examples/WiFiClientEnterprise/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClientEnterprise/ci.yml b/libraries/WiFi/examples/WiFiClientEnterprise/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/WiFi/examples/WiFiClientEnterprise/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/WiFi/examples/WiFiClientEvents/ci.json b/libraries/WiFi/examples/WiFiClientEvents/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiClientEvents/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClientEvents/ci.yml b/libraries/WiFi/examples/WiFiClientEvents/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiClientEvents/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiClientStaticIP/ci.json b/libraries/WiFi/examples/WiFiClientStaticIP/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiClientStaticIP/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiClientStaticIP/ci.yml b/libraries/WiFi/examples/WiFiClientStaticIP/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiClientStaticIP/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiExtender/ci.json b/libraries/WiFi/examples/WiFiExtender/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiExtender/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiExtender/ci.yml b/libraries/WiFi/examples/WiFiExtender/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiExtender/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiIPv6/ci.json b/libraries/WiFi/examples/WiFiIPv6/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiIPv6/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiIPv6/ci.yml b/libraries/WiFi/examples/WiFiIPv6/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiIPv6/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiMulti/ci.json b/libraries/WiFi/examples/WiFiMulti/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiMulti/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiMulti/ci.yml b/libraries/WiFi/examples/WiFiMulti/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiMulti/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/ci.json b/libraries/WiFi/examples/WiFiMultiAdvanced/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiMultiAdvanced/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiMultiAdvanced/ci.yml b/libraries/WiFi/examples/WiFiMultiAdvanced/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiMultiAdvanced/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiScan/ci.json b/libraries/WiFi/examples/WiFiScan/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiScan/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiScan/ci.yml b/libraries/WiFi/examples/WiFiScan/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiScan/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiScanAsync/ci.json b/libraries/WiFi/examples/WiFiScanAsync/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiScanAsync/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiScanAsync/ci.yml b/libraries/WiFi/examples/WiFiScanAsync/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanAsync/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiScanDualAntenna/ci.json b/libraries/WiFi/examples/WiFiScanDualAntenna/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiScanDualAntenna/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiScanDualAntenna/ci.yml b/libraries/WiFi/examples/WiFiScanDualAntenna/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanDualAntenna/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiScanTime/ci.json b/libraries/WiFi/examples/WiFiScanTime/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiScanTime/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiScanTime/ci.yml b/libraries/WiFi/examples/WiFiScanTime/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiScanTime/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiSmartConfig/ci.json b/libraries/WiFi/examples/WiFiSmartConfig/ci.json deleted file mode 100644 index 36babb82730..00000000000 --- a/libraries/WiFi/examples/WiFiSmartConfig/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiSmartConfig/ci.yml b/libraries/WiFi/examples/WiFiSmartConfig/ci.yml new file mode 100644 index 00000000000..86e194b136b --- /dev/null +++ b/libraries/WiFi/examples/WiFiSmartConfig/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/WiFi/examples/WiFiTelnetToSerial/ci.json b/libraries/WiFi/examples/WiFiTelnetToSerial/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiTelnetToSerial/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiTelnetToSerial/ci.yml b/libraries/WiFi/examples/WiFiTelnetToSerial/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiTelnetToSerial/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiUDPClient/ci.json b/libraries/WiFi/examples/WiFiUDPClient/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WiFi/examples/WiFiUDPClient/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "requires_any": [ - "CONFIG_SOC_WIFI_SUPPORTED=y", - "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" - ] -} diff --git a/libraries/WiFi/examples/WiFiUDPClient/ci.yml b/libraries/WiFi/examples/WiFiUDPClient/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/WiFi/examples/WiFiUDPClient/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/WiFi/examples/WiFiUDPClient/udp_server.py b/libraries/WiFi/examples/WiFiUDPClient/udp_server.py index c70a6fe2c37..48ab8f78628 100644 --- a/libraries/WiFi/examples/WiFiUDPClient/udp_server.py +++ b/libraries/WiFi/examples/WiFiUDPClient/udp_server.py @@ -2,6 +2,96 @@ # for messages from the ESP32 board and prints them import socket import sys +import subprocess +import platform + + +def get_interface_ips(): + """Get all available interface IP addresses""" + interface_ips = [] + + # Try using system commands to get interface IPs + system = platform.system().lower() + + try: + if system == "darwin" or system == "linux": + # Use 'ifconfig' on macOS/Linux + result = subprocess.run(["ifconfig"], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + lines = result.stdout.split("\n") + for line in lines: + if "inet " in line and "127.0.0.1" not in line: + # Extract IP address from ifconfig output + parts = line.strip().split() + for i, part in enumerate(parts): + if part == "inet": + if i + 1 < len(parts): + ip = parts[i + 1] + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + break + elif system == "windows": + # Use 'ipconfig' on Windows + result = subprocess.run(["ipconfig"], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + lines = result.stdout.split("\n") + for line in lines: + if "IPv4 Address" in line and "127.0.0.1" not in line: + # Extract IP address from ipconfig output + if ":" in line: + ip = line.split(":")[1].strip() + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError): + print("Error: Failed to get interface IPs using system commands") + print("Trying fallback methods...") + + # Fallback: try to get IPs using socket methods + if not interface_ips: + try: + # Get all IP addresses associated with the hostname + hostname = socket.gethostname() + ip_list = socket.gethostbyname_ex(hostname)[2] + for ip in ip_list: + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + except socket.gaierror: + print("Error: Failed to get interface IPs using sockets") + + # Fail if no interfaces found + if not interface_ips: + print("Error: No network interfaces found. Please check your network configuration.") + sys.exit(1) + + return interface_ips + + +def select_interface(interface_ips): + """Ask user to select which interface to bind to""" + if len(interface_ips) == 1: + print(f"Using interface: {interface_ips[0]}") + return interface_ips[0] + + print("Multiple network interfaces detected:") + for i, ip in enumerate(interface_ips, 1): + print(f" {i}. {ip}") + + while True: + try: + choice = input(f"Select interface (1-{len(interface_ips)}): ").strip() + choice_idx = int(choice) - 1 + if 0 <= choice_idx < len(interface_ips): + selected_ip = interface_ips[choice_idx] + print(f"Selected interface: {selected_ip}") + return selected_ip + else: + print(f"Please enter a number between 1 and {len(interface_ips)}") + except ValueError: + print("Please enter a valid number") + except KeyboardInterrupt: + print("\nExiting...") + sys.exit(1) + try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -10,15 +100,17 @@ print("Failed to create socket. Error Code : " + str(msg[0]) + " Message " + msg[1]) sys.exit() +# Get available interfaces and let user choose +interface_ips = get_interface_ips() +selected_ip = select_interface(interface_ips) + try: - s.bind(("", 3333)) + s.bind((selected_ip, 3333)) except socket.error as msg: print("Bind failed. Error: " + str(msg[0]) + ": " + msg[1]) sys.exit() -print("Server listening") - -print("Server listening") +print(f"Server listening on {selected_ip}:3333") while 1: d = s.recvfrom(1024) diff --git a/libraries/WiFi/library.properties b/libraries/WiFi/library.properties index 82ccb32b702..61e8ddb0ca5 100644 --- a/libraries/WiFi/library.properties +++ b/libraries/WiFi/library.properties @@ -1,5 +1,5 @@ name=WiFi -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Enables network connection (local and Internet) using the ESP32 built-in WiFi. diff --git a/libraries/WiFi/src/STA.cpp b/libraries/WiFi/src/STA.cpp index 84258589b28..36577a38b83 100644 --- a/libraries/WiFi/src/STA.cpp +++ b/libraries/WiFi/src/STA.cpp @@ -525,25 +525,6 @@ bool STAClass::connect( #endif /* CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT */ bool STAClass::disconnect(bool eraseap, unsigned long timeout) { - if (eraseap) { - if (!started()) { - log_e("STA not started! You must call begin first."); - return false; - } - wifi_config_t conf; - memset(&conf, 0, sizeof(wifi_config_t)); - esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &conf); - if (err != ESP_OK) { - log_e("STA clear config failed! 0x%x: %s", err, esp_err_to_name(err)); - return false; - } - } - - if (!connected()) { - log_w("STA already disconnected."); - return true; - } - esp_err_t err = esp_wifi_disconnect(); if (err != ESP_OK) { log_e("STA disconnect failed! 0x%x: %s", err, esp_err_to_name(err)); @@ -560,6 +541,20 @@ bool STAClass::disconnect(bool eraseap, unsigned long timeout) { } } + if (eraseap) { + if (!started()) { + log_e("STA not started! You must call begin first."); + return false; + } + wifi_config_t conf; + memset(&conf, 0, sizeof(wifi_config_t)); + esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &conf); + if (err != ESP_OK) { + log_e("STA clear config failed! 0x%x: %s", err, esp_err_to_name(err)); + return false; + } + } + return true; } diff --git a/libraries/WiFi/src/WiFi.cpp b/libraries/WiFi/src/WiFi.cpp index 7fb0ed16459..2db8dba9ed6 100644 --- a/libraries/WiFi/src/WiFi.cpp +++ b/libraries/WiFi/src/WiFi.cpp @@ -45,13 +45,13 @@ extern "C" { * @param p Print interface */ void WiFiClass::printDiag(Print &p) { - const char *modes[] = {"NULL", "STA", "AP", "STA+AP"}; + const char *modes[] = {"NULL", "STA", "AP", "STA+AP", "NAN"}; - wifi_mode_t mode; + wifi_mode_t mode = WIFI_MODE_NULL; esp_wifi_get_mode(&mode); - uint8_t primaryChan; - wifi_second_chan_t secondChan; + uint8_t primaryChan = 0; + wifi_second_chan_t secondChan = WIFI_SECOND_CHAN_NONE; esp_wifi_get_channel(&primaryChan, &secondChan); p.print("Mode: "); @@ -67,7 +67,7 @@ void WiFiClass::printDiag(Print &p) { p.println(wifi_station_get_connect_status()); */ - wifi_config_t conf; + wifi_config_t conf = {0}; esp_wifi_get_config((wifi_interface_t)WIFI_IF_STA, &conf); const char *ssid = reinterpret_cast(conf.sta.ssid); diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 66f8908af77..43d05c03b61 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -68,6 +68,7 @@ esp_netif_t *get_esp_interface_netif(esp_interface_t interface) { } static void _arduino_event_cb(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + (void)arg; arduino_event_t arduino_event; arduino_event.event_id = ARDUINO_EVENT_MAX; @@ -240,100 +241,18 @@ extern "C" void phy_bbpll_en_usb(bool en); #endif #if CONFIG_ESP_WIFI_REMOTE_ENABLED -extern "C" { -//#include "esp_hosted.h" -#include "esp_hosted_transport_config.h" -extern esp_err_t esp_hosted_init(); -extern esp_err_t esp_hosted_deinit(); -}; -typedef struct { - uint8_t pin_clk; - uint8_t pin_cmd; - uint8_t pin_d0; - uint8_t pin_d1; - uint8_t pin_d2; - uint8_t pin_d3; - uint8_t pin_reset; -} sdio_pin_config_t; - -static bool hosted_initialized = false; -static sdio_pin_config_t sdio_pin_config = { -#ifdef BOARD_HAS_SDIO_ESP_HOSTED - .pin_clk = BOARD_SDIO_ESP_HOSTED_CLK, - .pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD, - .pin_d0 = BOARD_SDIO_ESP_HOSTED_D0, - .pin_d1 = BOARD_SDIO_ESP_HOSTED_D1, - .pin_d2 = BOARD_SDIO_ESP_HOSTED_D2, - .pin_d3 = BOARD_SDIO_ESP_HOSTED_D3, - .pin_reset = BOARD_SDIO_ESP_HOSTED_RESET -#else - .pin_clk = CONFIG_ESP_SDIO_PIN_CLK, - .pin_cmd = CONFIG_ESP_SDIO_PIN_CMD, - .pin_d0 = CONFIG_ESP_SDIO_PIN_D0, - .pin_d1 = CONFIG_ESP_SDIO_PIN_D1, - .pin_d2 = CONFIG_ESP_SDIO_PIN_D2, - .pin_d3 = CONFIG_ESP_SDIO_PIN_D3, - .pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE -#endif -}; bool WiFiGenericClass::setPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { - if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) { - log_e("All SDIO pins must be defined"); - return false; - } - if (hosted_initialized) { - log_e("SDIO pins must be set before WiFi is initialized"); - return false; - } - sdio_pin_config.pin_clk = clk; - sdio_pin_config.pin_cmd = cmd; - sdio_pin_config.pin_d0 = d0; - sdio_pin_config.pin_d1 = d1; - sdio_pin_config.pin_d2 = d2; - sdio_pin_config.pin_d3 = d3; - sdio_pin_config.pin_reset = rst; - return true; + return hostedSetPins(clk, cmd, d0, d1, d2, d3, rst); } -static bool wifiHostedInit() { - if (!hosted_initialized) { - hosted_initialized = true; - struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); - conf.pin_clk.pin = sdio_pin_config.pin_clk; - conf.pin_cmd.pin = sdio_pin_config.pin_cmd; - conf.pin_d0.pin = sdio_pin_config.pin_d0; - conf.pin_d1.pin = sdio_pin_config.pin_d1; - conf.pin_d2.pin = sdio_pin_config.pin_d2; - conf.pin_d3.pin = sdio_pin_config.pin_d3; - conf.pin_reset.pin = sdio_pin_config.pin_reset; - // esp_hosted_sdio_set_config() will fail on second attempt but here temporarily to not cause exception on reinit - if (esp_hosted_sdio_set_config(&conf) != ESP_OK || esp_hosted_init() != ESP_OK) { - log_e("esp_hosted_init failed!"); - hosted_initialized = false; - return false; - } - log_v("ESP-HOSTED initialized!"); - } - // Attach pins to PeriMan here - // Slave chip model is CONFIG_IDF_SLAVE_TARGET - // sdio_pin_config.pin_clk - // sdio_pin_config.pin_cmd - // sdio_pin_config.pin_d0 - // sdio_pin_config.pin_d1 - // sdio_pin_config.pin_d2 - // sdio_pin_config.pin_d3 - // sdio_pin_config.pin_reset - - return true; -} #endif bool wifiLowLevelInit(bool persistent) { if (!lowLevelInitDone) { lowLevelInitDone = true; #if CONFIG_ESP_WIFI_REMOTE_ENABLED - if (!wifiHostedInit()) { + if (!hostedInitWiFi()) { lowLevelInitDone = false; return lowLevelInitDone; } @@ -345,6 +264,12 @@ bool wifiLowLevelInit(bool persistent) { wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); +#if CONFIG_ESP_WIFI_REMOTE_ENABLED + // required for proper work when esp-hosted is used. + cfg.nvs_enable = false; + persistent = false; +#endif + if (!WiFiGenericClass::useStaticBuffers()) { cfg.static_tx_buf_num = 0; cfg.dynamic_tx_buf_num = 32; @@ -402,11 +327,7 @@ static bool wifiLowLevelDeinit() { arduino_event.event_id = ARDUINO_EVENT_WIFI_OFF; Network.postEvent(&arduino_event); #if CONFIG_ESP_WIFI_REMOTE_ENABLED - if (hosted_initialized && esp_hosted_deinit() == ESP_OK) { - hosted_initialized = false; - log_v("ESP-HOSTED uninitialized!"); - // detach SDIO pins from PeriMan - } + hostedDeinitWiFi(); #endif } } @@ -454,7 +375,6 @@ static bool espWiFiStop() { bool WiFiGenericClass::_persistent = true; bool WiFiGenericClass::_long_range = false; -wifi_mode_t WiFiGenericClass::_forceSleepLastMode = WIFI_MODE_NULL; #if CONFIG_IDF_TARGET_ESP32S2 wifi_ps_type_t WiFiGenericClass::_sleepEnabled = WIFI_PS_NONE; #else @@ -745,7 +665,7 @@ bool WiFiGenericClass::enableAP(bool enable) { } /** - * control modem sleep when only in STA mode + * Enable or disable WiFi modem power save mode * @param enable bool * @return ok */ @@ -754,28 +674,33 @@ bool WiFiGenericClass::setSleep(bool enabled) { } /** - * control modem sleep when only in STA mode + * Set WiFi modem power save mode * @param mode wifi_ps_type_t * @return ok */ bool WiFiGenericClass::setSleep(wifi_ps_type_t sleepType) { - if (sleepType != _sleepEnabled) { + if (sleepType > WIFI_PS_MAX_MODEM) { + return false; + } + + if (!WiFi.STA.started()) { _sleepEnabled = sleepType; - if (WiFi.STA.started()) { - esp_err_t err = esp_wifi_set_ps(_sleepEnabled); - if (err != ESP_OK) { - log_e("esp_wifi_set_ps failed!: 0x%x: %s", err, esp_err_to_name(err)); - return false; - } - } return true; } - return false; + + esp_err_t err = esp_wifi_set_ps(_sleepEnabled); + if (err != ESP_OK) { + log_e("esp_wifi_set_ps failed!: 0x%x: %s", err, esp_err_to_name(err)); + return false; + } + + _sleepEnabled = sleepType; + return true; } /** - * get modem sleep enabled - * @return true if modem sleep is enabled + * Get WiFi modem power save mode + * @return wifi_ps_type_t */ wifi_ps_type_t WiFiGenericClass::getSleep() { return _sleepEnabled; diff --git a/libraries/WiFi/src/WiFiGeneric.h b/libraries/WiFi/src/WiFiGeneric.h index cdc1519d30b..4270e4d70d8 100644 --- a/libraries/WiFi/src/WiFiGeneric.h +++ b/libraries/WiFi/src/WiFiGeneric.h @@ -149,7 +149,6 @@ class WiFiGenericClass { protected: static bool _persistent; static bool _long_range; - static wifi_mode_t _forceSleepLastMode; static wifi_ps_type_t _sleepEnabled; static bool _wifiUseStaticBuffers; diff --git a/libraries/WiFi/src/WiFiScan.cpp b/libraries/WiFi/src/WiFiScan.cpp index 086b875fcb2..b9978d8e0ad 100644 --- a/libraries/WiFi/src/WiFiScan.cpp +++ b/libraries/WiFi/src/WiFiScan.cpp @@ -113,6 +113,9 @@ int16_t * @param status STATUS */ void WiFiScanClass::_scanDone() { + if (!(WiFiGenericClass::getStatusBits() & WIFI_SCANNING_BIT)) { + return; //Ignore if not scanning, scan was started by other + } esp_wifi_scan_get_ap_num(&(WiFiScanClass::_scanCount)); if (WiFiScanClass::_scanResult) { free(WiFiScanClass::_scanResult); diff --git a/libraries/WiFiProv/examples/WiFiProv/ci.json b/libraries/WiFiProv/examples/WiFiProv/ci.json deleted file mode 100644 index 04eb62b977a..00000000000 --- a/libraries/WiFiProv/examples/WiFiProv/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/libraries/WiFiProv/examples/WiFiProv/ci.yml b/libraries/WiFiProv/examples/WiFiProv/ci.yml new file mode 100644 index 00000000000..e412162e577 --- /dev/null +++ b/libraries/WiFiProv/examples/WiFiProv/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=huge_app + +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/libraries/WiFiProv/library.properties b/libraries/WiFiProv/library.properties index 1b19186c40b..0a4aa5f9837 100644 --- a/libraries/WiFiProv/library.properties +++ b/libraries/WiFiProv/library.properties @@ -1,5 +1,5 @@ name=WiFiProv -version=3.3.0 +version=3.3.4 author=Switi Mhaiske maintainer=Hristo Gochkov sentence=Enables provisioning. diff --git a/libraries/WiFiProv/src/WiFiProv.h b/libraries/WiFiProv/src/WiFiProv.h index d34727b6896..0ebc5956682 100644 --- a/libraries/WiFiProv/src/WiFiProv.h +++ b/libraries/WiFiProv/src/WiFiProv.h @@ -65,6 +65,8 @@ class WiFiProvClass { void printQR(const char *name, const char *pop, const char *transport, Print &out = Serial); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIFIPROV) extern WiFiProvClass WiFiProv; +#endif #endif /* SOC_WIFI_SUPPORTED */ diff --git a/libraries/Wire/examples/WireMaster/ci.json b/libraries/Wire/examples/WireMaster/ci.json deleted file mode 100644 index 1844adfc786..00000000000 --- a/libraries/Wire/examples/WireMaster/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2C_SUPPORTED=y" - ] -} diff --git a/libraries/Wire/examples/WireMaster/ci.yml b/libraries/Wire/examples/WireMaster/ci.yml new file mode 100644 index 00000000000..f9928773b30 --- /dev/null +++ b/libraries/Wire/examples/WireMaster/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_I2C_SUPPORTED=y diff --git a/libraries/Wire/examples/WireScan/ci.json b/libraries/Wire/examples/WireScan/ci.json deleted file mode 100644 index 1844adfc786..00000000000 --- a/libraries/Wire/examples/WireScan/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2C_SUPPORTED=y" - ] -} diff --git a/libraries/Wire/examples/WireScan/ci.yml b/libraries/Wire/examples/WireScan/ci.yml new file mode 100644 index 00000000000..f9928773b30 --- /dev/null +++ b/libraries/Wire/examples/WireScan/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_I2C_SUPPORTED=y diff --git a/libraries/Wire/examples/WireSlave/ci.json b/libraries/Wire/examples/WireSlave/ci.json deleted file mode 100644 index 3c877975d62..00000000000 --- a/libraries/Wire/examples/WireSlave/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2C_SUPPORT_SLAVE=y" - ] -} diff --git a/libraries/Wire/examples/WireSlave/ci.yml b/libraries/Wire/examples/WireSlave/ci.yml new file mode 100644 index 00000000000..40e259fda10 --- /dev/null +++ b/libraries/Wire/examples/WireSlave/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_I2C_SUPPORT_SLAVE=y diff --git a/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json b/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json deleted file mode 100644 index 3c877975d62..00000000000 --- a/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_I2C_SUPPORT_SLAVE=y" - ] -} diff --git a/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.yml b/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.yml new file mode 100644 index 00000000000..40e259fda10 --- /dev/null +++ b/libraries/Wire/examples/WireSlaveFunctionalCallback/ci.yml @@ -0,0 +1,2 @@ +requires: + - CONFIG_SOC_I2C_SUPPORT_SLAVE=y diff --git a/libraries/Wire/library.properties b/libraries/Wire/library.properties index 182e98790bc..f016c349b88 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=3.3.0 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=Allows the communication between devices or sensors connected via Two Wire Interface Bus. For esp8266 boards. diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 9cebdfaa304..91a9ddc44bb 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -142,6 +142,7 @@ class TwoWire : public HardwareI2C { #endif /* SOC_I2C_SUPPORT_SLAVE */ }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIRE) extern TwoWire Wire; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) #if SOC_I2C_NUM > 1 @@ -154,6 +155,7 @@ extern TwoWire Wire2; extern TwoWire Wire1; #endif /* SOC_HP_I2C_NUM */ #endif +#endif #endif /* SOC_I2C_SUPPORTED */ #endif /* TwoWire_h */ diff --git a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.yml b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Analog_Input_Output/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json b/libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/README.md b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md similarity index 78% rename from libraries/Zigbee/examples/Zigbee_Binary_Input/README.md rename to libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md index 6ca3aac7119..18b869446f1 100644 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input/README.md +++ b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md @@ -1,6 +1,6 @@ -# Arduino-ESP32 Zigbee Binary Input Example +# Arduino-ESP32 Zigbee Binary Input Output Example -This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input device with two different applications: HVAC fan status and security zone armed status. +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input/output device with multiple applications: HVAC fan status/control, security zone armed status, and HVAC humidifier control. # Supported Targets @@ -9,12 +9,17 @@ Currently, this example supports the following targets. | Supported Targets | ESP32-C6 | ESP32-H2 | | ----------------- | -------- | -------- | -## Binary Input Functions - - * The example implements two binary inputs: - - HVAC Fan Status: Reports the current state of a fan - - Security Zone Armed: Reports the armed state of a security zone - * By clicking the button (BOOT) on this board, it will toggle both binary inputs and immediately send a report of their states to the network. +## Binary Input/Output Functions + + * The example implements three binary devices: + - **Binary Fan Device (Endpoint 1)**: + - Binary Input: HVAC Fan Status - Reports the current state of a fan + - Binary Output: HVAC Fan - Controls the fan switch with callback function + - **Binary Zone Device (Endpoint 2)**: + - Binary Input: Security Zone Armed - Reports the armed state of a security zone + - **Binary Humidifier Device (Endpoint 3)**: + - Binary Output: HVAC Humidifier - Controls the humidifier switch with callback function + * By clicking the button (BOOT) on this board, it will toggle all binary inputs/outputs and immediately send a report of their states to the network. * Holding the button for more than 3 seconds will trigger a factory reset of the Zigbee device. ## Hardware Required diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino similarity index 67% rename from libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino rename to libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino index de0cf606dcd..60ae2735131 100644 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino +++ b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino @@ -13,9 +13,9 @@ // limitations under the License. /** - * @brief This example demonstrates Zigbee binary input device. + * @brief This example demonstrates Zigbee binary input/output device. * - * The example demonstrates how to use Zigbee library to create an end device binary sensor device. + * The example demonstrates how to use Zigbee library to create an end device binary sensor/switch device. * * Proper Zigbee mode must be selected in Tools->Zigbee mode * and also the correct partition scheme must be selected in Tools->Partition Scheme. @@ -34,13 +34,24 @@ /* Zigbee binary sensor device configuration */ #define BINARY_DEVICE_ENDPOINT_NUMBER 1 -uint8_t binaryPin = A0; uint8_t button = BOOT_PIN; ZigbeeBinary zbBinaryFan = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER); ZigbeeBinary zbBinaryZone = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 1); +ZigbeeBinary zbBinaryHumidifier = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 2); -bool binaryStatus = false; +bool zoneStatus = false; + +void fanSwitch(bool state) { + Serial.println("Fan switch changed to: " + String(state)); + // Switch Fan status input signaling the fan status has changed + zbBinaryFan.setBinaryInput(state); + zbBinaryFan.reportBinaryInput(); +} + +void humidifierSwitch(bool state) { + Serial.println("Humidifier switch changed to: " + String(state)); +} void setup() { Serial.begin(115200); @@ -55,19 +66,33 @@ void setup() { // Optional: set Zigbee device name and model zbBinaryFan.setManufacturerAndModel("Espressif", "ZigbeeBinarySensor"); - // Set up binary fan status input (HVAC) + // Set up binary fan status input + switch output (HVAC) zbBinaryFan.addBinaryInput(); zbBinaryFan.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS); zbBinaryFan.setBinaryInputDescription("Fan Status"); + zbBinaryFan.addBinaryOutput(); + zbBinaryFan.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN); + zbBinaryFan.setBinaryOutputDescription("Fan Switch"); + + zbBinaryFan.onBinaryOutputChange(fanSwitch); + // Set up binary zone armed input (Security) zbBinaryZone.addBinaryInput(); zbBinaryZone.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED); zbBinaryZone.setBinaryInputDescription("Zone Armed"); + // Set up binary humidifier output (HVAC) + zbBinaryHumidifier.addBinaryOutput(); + zbBinaryHumidifier.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER); + zbBinaryHumidifier.setBinaryOutputDescription("Humidifier Switch"); + + zbBinaryHumidifier.onBinaryOutputChange(humidifierSwitch); + // Add endpoints to Zigbee Core Zigbee.addEndpoint(&zbBinaryFan); Zigbee.addEndpoint(&zbBinaryZone); + Zigbee.addEndpoint(&zbBinaryHumidifier); Serial.println("Starting Zigbee..."); // When all EPs are registered, start Zigbee in End Device mode @@ -101,12 +126,19 @@ void loop() { Zigbee.factoryReset(); } } - // Toggle binary input - binaryStatus = !binaryStatus; - zbBinaryFan.setBinaryInput(binaryStatus); - zbBinaryZone.setBinaryInput(binaryStatus); - zbBinaryFan.reportBinaryInput(); + + // Toggle fan + zbBinaryFan.setBinaryOutput(!zbBinaryFan.getBinaryOutput()); + zbBinaryFan.reportBinaryOutput(); + + // Toggle zone + zoneStatus = !zoneStatus; + zbBinaryZone.setBinaryInput(zoneStatus); zbBinaryZone.reportBinaryInput(); + + // Toggle humidifier + zbBinaryHumidifier.setBinaryOutput(!zbBinaryHumidifier.getBinaryOutput()); + zbBinaryHumidifier.reportBinaryOutput(); } delay(100); } diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.yml b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_CarbonDioxide_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/Zigbee_Color_Dimmable_Light.ino b/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/Zigbee_Color_Dimmable_Light.ino index e84720d4863..5549fcca959 100644 --- a/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/Zigbee_Color_Dimmable_Light.ino +++ b/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/Zigbee_Color_Dimmable_Light.ino @@ -13,10 +13,10 @@ // limitations under the License. /** - * @brief This example demonstrates Zigbee Color Dimmable light bulb. + * @brief This example demonstrates Zigbee Color Dimmable light bulb with RGB and Temperature support. * * The example demonstrates how to use Zigbee library to create an end device with - * color dimmable light end point. + * color dimmable light end point supporting both RGB (X/Y) and Color Temperature modes. * The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator. * * Proper Zigbee mode must be selected in Tools->Zigbee mode @@ -40,6 +40,15 @@ uint8_t button = BOOT_PIN; ZigbeeColorDimmableLight zbColorLight = ZigbeeColorDimmableLight(ZIGBEE_RGB_LIGHT_ENDPOINT); +/********************* Temperature conversion functions **************************/ +uint16_t kelvinToMireds(uint16_t kelvin) { + return 1000000 / kelvin; +} + +uint16_t miredsToKelvin(uint16_t mireds) { + return 1000000 / mireds; +} + /********************* RGB LED functions **************************/ void setRGBLight(bool state, uint8_t red, uint8_t green, uint8_t blue, uint8_t level) { if (!state) { @@ -50,6 +59,20 @@ void setRGBLight(bool state, uint8_t red, uint8_t green, uint8_t blue, uint8_t l rgbLedWrite(led, red * brightness, green * brightness, blue * brightness); } +/********************* Temperature LED functions **************************/ +void setTempLight(bool state, uint8_t level, uint16_t mireds) { + if (!state) { + rgbLedWrite(led, 0, 0, 0); + return; + } + float brightness = (float)level / 255; + // Convert mireds to color temperature (K) and map to white/yellow + uint16_t kelvin = miredsToKelvin(mireds); + uint8_t warm = constrain(map(kelvin, 2000, 6500, 255, 0), 0, 255); + uint8_t cold = constrain(map(kelvin, 2000, 6500, 0, 255), 0, 255); + rgbLedWrite(led, warm * brightness, warm * brightness, cold * brightness); +} + // Create a task on identify call to handle the identify function void identify(uint16_t time) { static uint8_t blink = 1; @@ -73,8 +96,13 @@ void setup() { // Init button for factory reset pinMode(button, INPUT_PULLUP); - // Set callback function for light change - zbColorLight.onLightChange(setRGBLight); + // Enable both XY (RGB) and Temperature color capabilities + uint16_t capabilities = ZIGBEE_COLOR_CAPABILITY_X_Y | ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP; + zbColorLight.setLightColorCapabilities(capabilities); + + // Set callback functions for RGB and Temperature modes + zbColorLight.onLightChangeRgb(setRGBLight); + zbColorLight.onLightChangeTemp(setTempLight); // Optional: Set callback function for device identify zbColorLight.onIdentify(identify); @@ -82,6 +110,9 @@ void setup() { // Optional: Set Zigbee device name and model zbColorLight.setManufacturerAndModel("Espressif", "ZBColorLightBulb"); + // Set min/max temperature range (High Kelvin -> Low Mireds: Min and Max is switched) + zbColorLight.setLightColorTemperatureRange(kelvinToMireds(6500), kelvinToMireds(2000)); + // Add endpoint to Zigbee Core Serial.println("Adding ZigbeeLight endpoint to Zigbee Core"); Zigbee.addEndpoint(&zbColorLight); diff --git a/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.json b/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.yml b/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Color_Dimmable_Light/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.json b/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.yml b/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Color_Dimmer_Switch/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino index ce9eedb683d..1a84c4d7471 100644 --- a/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/Zigbee_Contact_Switch.ino @@ -31,17 +31,25 @@ #endif #include "Zigbee.h" +#include /* Zigbee contact sensor configuration */ -#define CONTACT_SWITCH_ENDPOINT_NUMBER 10 +#define CONTACT_SWITCH_ENDPOINT_NUMBER 1 uint8_t button = BOOT_PIN; uint8_t sensor_pin = 4; ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER); +/* Preferences for storing ENROLLED flag to persist across reboots */ +Preferences preferences; + void setup() { Serial.begin(115200); + preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots + bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences + preferences.end(); + // Init button + switch pinMode(button, INPUT_PULLUP); pinMode(sensor_pin, INPUT_PULLUP); @@ -67,6 +75,31 @@ void setup() { delay(100); } Serial.println(); + + // Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll + if (enrolled) { + Serial.println("Device has been enrolled before - restoring IAS Zone enrollment"); + zbContactSwitch.restoreIASZoneEnroll(); + } else { + Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment"); + zbContactSwitch.requestIASZoneEnroll(); + } + + while (!zbContactSwitch.enrolled()) { + Serial.print("."); + delay(100); + } + Serial.println(); + Serial.println("Zigbee enrolled successfully!"); + + // Store ENROLLED flag only if this was a new enrollment (previous flag was false) + // Skip writing if we just restored enrollment (flag was already true) + if (!enrolled) { + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", true); // set ENROLLED flag to true + preferences.end(); + Serial.println("ENROLLED flag saved to preferences"); + } } void loop() { @@ -91,6 +124,11 @@ void loop() { if ((millis() - startTime) > 3000) { // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + // Clear the ENROLLED flag from preferences + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", false); // set ENROLLED flag to false + preferences.end(); + Serial.println("ENROLLED flag cleared from preferences"); delay(1000); Zigbee.factoryReset(); } diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json b/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.yml b/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Contact_Switch/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.json b/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.yml b/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Dimmable_Light/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.json b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.yml b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Electrical_AC_Sensor_MultiPhase/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Electrical_DC_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.json b/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.yml b/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Fan_Control/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Gateway/ci.json b/libraries/Zigbee/examples/Zigbee_Gateway/ci.json deleted file mode 100644 index 23e1c59d1da..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Gateway/ci.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr_8MB,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ], - "targets": { - "esp32c6": false, - "esp32h2": false - } -} diff --git a/libraries/Zigbee/examples/Zigbee_Gateway/ci.yml b/libraries/Zigbee/examples/Zigbee_Gateway/ci.yml new file mode 100644 index 00000000000..ab3b5078f03 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Gateway/ci.yml @@ -0,0 +1,8 @@ +fqbn_append: PartitionScheme=zigbee_zczr_8MB,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y + +targets: + esp32c6: false + esp32h2: false diff --git a/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Illuminance_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md new file mode 100644 index 00000000000..662ebe780f7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md @@ -0,0 +1,90 @@ +# Arduino-ESP32 Zigbee Multistate Input Output Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) multistate input/output device. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Multistate Device Functions + +This example demonstrates two different multistate devices: + +1. **Standard Multistate Device** (`zbMultistateDevice`): Uses predefined application states from the Zigbee specification + - Application Type 0: Fan states (Off, On, Auto) + - Application Type 7: Light states (High, Normal, Low) + +2. **Custom Multistate Device** (`zbMultistateDeviceCustom`): Uses user-defined custom states + - Custom fan states: Off, On, UltraSlow, Slow, Fast, SuperFast + +* After this board first starts up, it will be configured as two multistate devices with different state configurations. +* By clicking the button (BOOT) on this board, the devices will cycle through their respective states and report the changes to the network. + +## Hardware Required + +* A USB cable for power supply and programming + +### Configure the Project + +Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2). + +The example creates two multistate devices: +- **Endpoint 1**: Standard multistate device using predefined Zigbee application types +- **Endpoint 2**: Custom multistate device using user-defined states + +You can modify the state names and configurations by changing the following variables: +- `multistate_custom_state_names[]`: Array of custom state names +- Application type and length macros: `ZB_MULTISTATE_APPLICATION_TYPE_X_STATE_NAMES`, +`ZB_MULTISTATE_APPLICATION_TYPE_X_NUM_STATES`, `ZB_MULTISTATE_APPLICATION_TYPE_X_INDEX` +- Device descriptions and application types in the setup() function + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the Coordinator/Router Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)` +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator. +You can do the following: + +* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`. +* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack. + +By default, the coordinator network is closed after rebooting or flashing new firmware. +To open the network you have 2 options: + +* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`. +* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join. + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino new file mode 100644 index 00000000000..d0f944e5e23 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino @@ -0,0 +1,189 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates Zigbee multistate input / output device. + * + * The example demonstrates how to use Zigbee library to create a router multistate device. + * In the example, we have two multistate devices: + * - zbMultistateDevice: uses defined application states from Zigbee specification + * - zbMultistateDeviceCustom: uses custom application states (user defined) + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * NOTE: HomeAssistant ZHA does not support multistate input and output clusters yet. + * + * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ZCZR +#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode" +#endif + +#include "Zigbee.h" + +/* Zigbee multistate device configuration */ +#define MULTISTATE_DEVICE_ENDPOINT_NUMBER 1 + +uint8_t button = BOOT_PIN; + +// zbMultistateDevice will use defined application states +ZigbeeMultistate zbMultistateDevice = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER); + +// zbMultistateDeviceCustom will use custom application states (user defined) +ZigbeeMultistate zbMultistateDeviceCustom = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER + 1); + +const char *multistate_custom_state_names[6] = {"Off", "On", "UltraSlow", "Slow", "Fast", "SuperFast"}; + +void onStateChange(uint16_t state) { + // print the state + Serial.printf("Received state change: %d\r\n", state); + // print the state name using the stored state names + const char *const *state_names = ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES; + if (state_names && state < zbMultistateDevice.getMultistateOutputStateNamesLength()) { + Serial.printf("State name: %s\r\n", state_names[state]); + } + // print state index of possible options + Serial.printf("State index: %d / %d\r\n", state, zbMultistateDevice.getMultistateOutputStateNamesLength() - 1); +} + +void onStateChangeCustom(uint16_t state) { + // print the state + Serial.printf("Received state change: %d\r\n", state); + // print the state name using the stored state names + if (state < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength()) { + Serial.printf("State name: %s\r\n", multistate_custom_state_names[state]); + } + // print state index of possible options + Serial.printf("State index: %d / %d\r\n", state, zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1); + + Serial.print("Changing to fan mode to: "); + switch (state) { + case 0: Serial.println("Off"); break; + case 1: Serial.println("On"); break; + case 2: Serial.println("UltraSlow"); break; + case 3: Serial.println("Slow"); break; + case 4: Serial.println("Fast"); break; + case 5: Serial.println("SuperFast"); break; + default: Serial.println("Invalid state"); break; + } +} + +void setup() { + log_d("Starting serial"); + Serial.begin(115200); + + // Init button switch + log_d("Init button switch"); + pinMode(button, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + log_d("Set Zigbee device name and model"); + zbMultistateDevice.setManufacturerAndModel("Espressif", "ZigbeeMultistateDevice"); + + // Set up analog input + log_d("Add Multistate Input"); + zbMultistateDevice.addMultistateInput(); + log_d("Set Multistate Input Application"); + zbMultistateDevice.setMultistateInputApplication(ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX); + log_d("Set Multistate Input Description"); + zbMultistateDevice.setMultistateInputDescription("Fan (on/off/auto)"); + zbMultistateDevice.setMultistateInputStates(ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES); + + // Set up analog output + log_d("Add Multistate Output"); + zbMultistateDevice.addMultistateOutput(); + log_d("Set Multistate Output Application"); + zbMultistateDevice.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX); + log_d("Set Multistate Output Description"); + zbMultistateDevice.setMultistateOutputDescription("Light (high/normal/low)"); + zbMultistateDevice.setMultistateOutputStates(ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES); + + // Set up custom output + log_d("Add Multistate Output"); + zbMultistateDeviceCustom.addMultistateOutput(); + log_d("Set Multistate Output Application"); + zbMultistateDeviceCustom.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX); + log_d("Set Multistate Output Description"); + zbMultistateDeviceCustom.setMultistateOutputDescription("Fan (on/off/slow/medium/fast)"); + zbMultistateDeviceCustom.setMultistateOutputStates(5); + + // Set callback function for multistate output change + log_d("Set callback function for multistate output change"); + zbMultistateDevice.onMultistateOutputChange(onStateChange); + zbMultistateDeviceCustom.onMultistateOutputChange(onStateChangeCustom); + + // Add endpoints to Zigbee Core + log_d("Add endpoints to Zigbee Core"); + Zigbee.addEndpoint(&zbMultistateDevice); + Zigbee.addEndpoint(&zbMultistateDeviceCustom); + + Serial.println("Starting Zigbee..."); + // When all EPs are registered, start Zigbee in Router Device mode + if (!Zigbee.begin(ZIGBEE_ROUTER)) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } else { + Serial.println("Zigbee started successfully!"); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println("Connected"); +} + +void loop() { + // Checking button for factory reset and reporting + if (digitalRead(button) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(button) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + delay(1000); + Zigbee.factoryReset(); + } + } + // For demonstration purposes, increment the multistate output/input value by 1 + if (zbMultistateDevice.getMultistateOutput() < zbMultistateDevice.getMultistateOutputStateNamesLength() - 1) { + zbMultistateDevice.setMultistateOutput(zbMultistateDevice.getMultistateOutput() + 1); + zbMultistateDevice.reportMultistateOutput(); + zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput()); + zbMultistateDevice.reportMultistateInput(); + } else { + zbMultistateDevice.setMultistateOutput(0); + zbMultistateDevice.reportMultistateOutput(); + zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput()); + zbMultistateDevice.reportMultistateInput(); + } + + if (zbMultistateDeviceCustom.getMultistateOutput() < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1) { + zbMultistateDeviceCustom.setMultistateOutput(zbMultistateDeviceCustom.getMultistateOutput() + 1); + zbMultistateDeviceCustom.reportMultistateOutput(); + } else { + zbMultistateDeviceCustom.setMultistateOutput(0); + zbMultistateDeviceCustom.reportMultistateOutput(); + } + } + delay(100); +} diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.yml b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino index 29d114014b4..6ea2329a459 100644 --- a/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino @@ -44,6 +44,18 @@ uint8_t button = BOOT_PIN; ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT); +volatile bool otaRunning = false; + +/********************* Callbacks *************************/ +void otaActiveCallback(bool otaActive) { + otaRunning = otaActive; + if (otaActive) { + Serial.println("OTA started"); + } else { + Serial.println("OTA finished"); + } +} + /********************* RGB LED functions **************************/ void setLED(bool value) { digitalWrite(led, value); @@ -69,6 +81,9 @@ void setup() { // Add OTA client to the light bulb zbLight.addOTAClient(OTA_UPGRADE_RUNNING_FILE_VERSION, OTA_UPGRADE_DOWNLOADED_FILE_VERSION, OTA_UPGRADE_HW_VERSION); + // Optional: Register callback for OTA state change + zbLight.onOTAStateChange(otaActiveCallback); + // Add endpoint to Zigbee Core Serial.println("Adding ZigbeeLight endpoint to Zigbee Core"); Zigbee.addEndpoint(&zbLight); @@ -99,6 +114,10 @@ void loop() { while (digitalRead(button) == LOW) { delay(50); if ((millis() - startTime) > 3000) { + if (otaRunning) { + Serial.println("OTA in progress, cannot reset now"); + break; + } // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); delay(1000); diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json b/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.yml b/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Occupancy_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.json b/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.yml b/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_On_Off_Light/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.json b/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.yml b/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_On_Off_MultiSwitch/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino index 0721371ce0e..d4a71bbed89 100644 --- a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino +++ b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/Zigbee_On_Off_Switch.ino @@ -66,6 +66,8 @@ static SwitchData buttonFunctionPair[] = {{GPIO_INPUT_IO_TOGGLE_SWITCH, SWITCH_O ZigbeeSwitch zbSwitch = ZigbeeSwitch(SWITCH_ENDPOINT_NUMBER); +static bool light_state = false; + /********************* Zigbee functions **************************/ static void onZbButton(SwitchData *button_func_pair) { if (button_func_pair->func == SWITCH_ONOFF_TOGGLE_CONTROL) { @@ -75,6 +77,33 @@ static void onZbButton(SwitchData *button_func_pair) { } } +static void onLightStateChange(bool state) { + if (state != light_state) { + light_state = state; + Serial.printf("Light state changed to %d\r\n", state); + } +} + +/********************* Periodic task ***************************/ +void periodicTask(void *arg) { + while (true) { + // print the bound lights every 10 seconds + static uint32_t lastPrint = 0; + if (millis() - lastPrint > 10000) { + lastPrint = millis(); + zbSwitch.printBoundDevices(Serial); + } + + // Poll light state every second + static uint32_t lastPoll = 0; + if (millis() - lastPoll > 1000) { + lastPoll = millis(); + zbSwitch.getLightState(); + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + /********************* GPIO functions **************************/ static QueueHandle_t gpio_evt_queue = NULL; @@ -102,6 +131,8 @@ void setup() { //Optional to allow multiple light to bind to the switch zbSwitch.allowMultipleBinding(true); + zbSwitch.onLightStateChange(onLightStateChange); + //Add endpoint to Zigbee Core Serial.println("Adding ZigbeeSwitch endpoint to Zigbee Core"); Zigbee.addEndpoint(&zbSwitch); @@ -154,6 +185,8 @@ void setup() { } Serial.println(); + + xTaskCreate(periodicTask, "periodicTask", 1024 * 4, NULL, 10, NULL); } void loop() { @@ -188,11 +221,4 @@ void loop() { } vTaskDelay(10 / portTICK_PERIOD_MS); } - - // print the bound lights every 10 seconds - static uint32_t lastPrint = 0; - if (millis() - lastPrint > 10000) { - lastPrint = millis(); - zbSwitch.printBoundDevices(Serial); - } } diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.json b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.yml b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_On_Off_Switch/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_PM25_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json b/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.yml b/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Power_Outlet/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Pressure_Flow_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.json b/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.yml b/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Range_Extender/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.json b/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.yml b/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Scan_Networks/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.json b/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.yml b/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino index 6f5934f791d..73125cbf5dc 100644 --- a/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino +++ b/libraries/Zigbee/examples/Zigbee_Thermostat/Zigbee_Thermostat.ino @@ -90,7 +90,7 @@ void setup() { #endif // Set callback function for receiving sensor configuration - zbThermostat.onConfigReceive(receiveSensorConfig); + zbThermostat.onTempConfigReceive(receiveSensorConfig); //Optional: set Zigbee device name and model zbThermostat.setManufacturerAndModel("Espressif", "ZigbeeThermostat"); @@ -138,10 +138,10 @@ void setup() { "Device on endpoint %d, IEEE Address: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\r\n", device->endpoint, device->ieee_addr[7], device->ieee_addr[6], device->ieee_addr[5], device->ieee_addr[4], device->ieee_addr[3], device->ieee_addr[2], device->ieee_addr[1], device->ieee_addr[0] ); - zbThermostat.getSensorSettings(device->endpoint, device->ieee_addr); + zbThermostat.getTemperatureSettings(device->endpoint, device->ieee_addr); } else { Serial.printf("Device on endpoint %d, short address: 0x%x\r\n", device->endpoint, device->short_addr); - zbThermostat.getSensorSettings(device->endpoint, device->short_addr); + zbThermostat.getTemperatureSettings(device->endpoint, device->short_addr); } } } diff --git a/libraries/Zigbee/examples/Zigbee_Thermostat/ci.json b/libraries/Zigbee/examples/Zigbee_Thermostat/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Thermostat/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", - "requires": [ - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Thermostat/ci.yml b/libraries/Zigbee/examples/Zigbee_Thermostat/ci.yml new file mode 100644 index 00000000000..2f21922223c --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Thermostat/ci.yml @@ -0,0 +1,4 @@ +fqbn_append: PartitionScheme=zigbee_zczr,ZigbeeMode=zczr + +requires: + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino index d9ac7b6e241..b3fc6b9d18b 100644 --- a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/Zigbee_Vibration_Sensor.ino @@ -31,17 +31,25 @@ #endif #include "Zigbee.h" +#include /* Zigbee vibration sensor configuration */ -#define VIBRATION_SENSOR_ENDPOINT_NUMBER 10 +#define VIBRATION_SENSOR_ENDPOINT_NUMBER 1 uint8_t button = BOOT_PIN; uint8_t sensor_pin = 4; ZigbeeVibrationSensor zbVibrationSensor = ZigbeeVibrationSensor(VIBRATION_SENSOR_ENDPOINT_NUMBER); +/* Preferences for storing ENROLLED flag to persist across reboots */ +Preferences preferences; + void setup() { Serial.begin(115200); + preferences.begin("Zigbee", false); // Save ENROLLED flag in flash so it persists across reboots + bool enrolled = preferences.getBool("ENROLLED"); // Get ENROLLED flag from preferences + preferences.end(); + // Init button + sensor pinMode(button, INPUT_PULLUP); pinMode(sensor_pin, INPUT); @@ -67,6 +75,31 @@ void setup() { delay(100); } Serial.println(); + + // Check if device has been enrolled before restarting - if so, restore IAS Zone enroll, otherwise request new IAS Zone enroll + if (enrolled) { + Serial.println("Device has been enrolled before - restoring IAS Zone enrollment"); + zbVibrationSensor.restoreIASZoneEnroll(); + } else { + Serial.println("Device is factory new - first time joining network - requesting new IAS Zone enrollment"); + zbVibrationSensor.requestIASZoneEnroll(); + } + + while (!zbVibrationSensor.enrolled()) { + Serial.print("."); + delay(100); + } + Serial.println(); + Serial.println("Zigbee enrolled successfully!"); + + // Store ENROLLED flag only if this was a new enrollment (previous flag was false) + // Skip writing if we just restored enrollment (flag was already true) + if (!enrolled) { + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", true); // set ENROLLED flag to true + preferences.end(); + Serial.println("ENROLLED flag saved to preferences"); + } } void loop() { @@ -95,6 +128,11 @@ void loop() { if ((millis() - startTime) > 3000) { // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + // Clear the ENROLLED flag from preferences + preferences.begin("Zigbee", false); + preferences.putBool("ENROLLED", false); // set ENROLLED flag to false + preferences.end(); + Serial.println("ENROLLED flag cleared from preferences"); delay(1000); Zigbee.factoryReset(); } diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Vibration_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.json b/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.yml b/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Wind_Speed_Sensor/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed", - "requires": [ - "CONFIG_SOC_IEEE802154_SUPPORTED=y", - "CONFIG_ZB_ENABLED=y" - ] -} diff --git a/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.yml b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.yml new file mode 100644 index 00000000000..22315a90f3b --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Window_Covering/ci.yml @@ -0,0 +1,5 @@ +fqbn_append: PartitionScheme=zigbee,ZigbeeMode=ed + +requires: + - CONFIG_SOC_IEEE802154_SUPPORTED=y + - CONFIG_ZB_ENABLED=y diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 68721c1a66f..9d981dcc39c 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -26,6 +26,7 @@ ZigbeeFlowSensor KEYWORD1 ZigbeeGateway KEYWORD1 ZigbeeIlluminanceSensor KEYWORD1 ZigbeeLight KEYWORD1 +ZigbeeMultistate KEYWORD1 ZigbeeOccupancySensor KEYWORD1 ZigbeePM25Sensor KEYWORD1 ZigbeePowerOutlet KEYWORD1 @@ -48,6 +49,7 @@ ZigbeeWindowCoveringType KEYWORD1 ZigbeeFanMode KEYWORD1 ZigbeeFanModeSequence KEYWORD1 zb_cmd_type_t KEYWORD1 +ZigbeeColorMode KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -55,6 +57,8 @@ zb_cmd_type_t KEYWORD1 # ZigbeeCore begin KEYWORD2 +start KEYWORD2 +stop KEYWORD2 started KEYWORD2 connected KEYWORD2 getRole KEYWORD2 @@ -73,6 +77,7 @@ scanComplete KEYWORD2 getScanResult KEYWORD2 scanDelete KEYWORD2 factoryReset KEYWORD2 +allowMultiEndpointBinding KEYWORD2 # Common ZigbeeEP setEpConfig KEYWORD2 @@ -100,17 +105,33 @@ clearBoundDevices KEYWORD2 onDefaultResponse KEYWORD2 # ZigbeeLight + ZigbeeColorDimmableLight -onLightChange KEYWORD2 restoreLight KEYWORD2 setLight KEYWORD2 setLightState KEYWORD2 setLightLevel KEYWORD2 setLightColor KEYWORD2 +setLightColorTemperature KEYWORD2 +setLightColorCapabilities KEYWORD2 +setLightColorTemperatureRange KEYWORD2 getLightState KEYWORD2 getLightLevel KEYWORD2 getLightRed KEYWORD2 getLightGreen KEYWORD2 getLightBlue KEYWORD2 +getLightColorTemperature KEYWORD2 +getLightColorMode KEYWORD2 +getLightColorHue KEYWORD2 +getLightColorSaturation KEYWORD2 +getLightColorCapabilities KEYWORD2 +onLightChange KEYWORD2 +onLightChangeRgb KEYWORD2 +onLightChangeHsv KEYWORD2 +onLightChangeTemp KEYWORD2 +onLightColorChangeWithSource KEYWORD2 +onLightLevelChange KEYWORD2 +onLightLevelChangeWithSource KEYWORD2 +onLightStateChange KEYWORD2 +onLightStateChangeWithSource KEYWORD2 # ZigbeeSwitch + ZigbeeColorDimmerSwitch lightToggle KEYWORD2 @@ -121,14 +142,29 @@ lightOnWithTimedOff KEYWORD2 lightOnWithSceneRecall KEYWORD2 setLightLevel KEYWORD2 setLightColor KEYWORD2 +getLightState KEYWORD2 +getLightLevel KEYWORD2 +getLightColor KEYWORD2 +onLightStateChange KEYWORD2 +onLightStateChangeWithSource KEYWORD2 +onLightLevelChange KEYWORD2 +onLightLevelChangeWithSource KEYWORD2 +onLightColorChange KEYWORD2 +onLightColorChangeWithSource KEYWORD2 # ZigbeeThermostat onTempRecieve KEYWORD2 -onConfigRecieve KEYWORD2 onTempReceiveWithSource KEYWORD2 +onTempConfigReceive KEYWORD2 getTemperature KEYWORD2 -getSensorSettings KEYWORD2 +getTemperatureSettings KEYWORD2 setTemperatureReporting KEYWORD2 +onHumidityReceive KEYWORD2 +onHumidityReceiveWithSource KEYWORD2 +onHumidityConfigReceive KEYWORD2 +getHumidity KEYWORD2 +getHumiditySettings KEYWORD2 +setHumidityReporting KEYWORD2 # Common Zigbee Sensor setMinMaxValue KEYWORD2 @@ -185,6 +221,9 @@ setIASClientEndpoint KEYWORD2 setClosed KEYWORD2 setOpen KEYWORD2 setTilted KEYWORD2 +requestIASZoneEnroll KEYWORD2 +restoreIASZoneEnroll KEYWORD2 +enrolled KEYWORD2 # ZigbeeVibrationSensor setVibration KEYWORD2 @@ -224,6 +263,27 @@ getFanMode KEYWORD2 getFanModeSequence KEYWORD2 onFanModeChange KEYWORD2 +# ZigbeeMultistate +addMultistateInput KEYWORD2 +addMultistateOutput KEYWORD2 +onMultistateOutputChange KEYWORD2 +setMultistateInput KEYWORD2 +getMultistateInput KEYWORD2 +setMultistateOutput KEYWORD2 +getMultistateOutput KEYWORD2 +reportMultistateInput KEYWORD2 +reportMultistateOutput KEYWORD2 +setMultistateInputApplication KEYWORD2 +setMultistateInputDescription KEYWORD2 +setMultistateInputStates KEYWORD2 +setMultistateOutputApplication KEYWORD2 +setMultistateOutputDescription KEYWORD2 +setMultistateOutputStates KEYWORD2 +#getMultistateInputStateNames KEYWORD2 +getMultistateInputStateNamesLength KEYWORD2 +#getMultistateOutputStateNames KEYWORD2 +getMultistateOutputStateNamesLength KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### @@ -238,3 +298,52 @@ ZIGBEE_DEFAULT_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_HOST_CONFIG LITERAL1 ZB_ARRAY_LENGHT LITERAL1 + +# ZigbeeMultistate +ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX LITERAL1 + +#ZigbeeColorDimmableLight +ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION LITERAL1 +ZIGBEE_COLOR_CAPABILITY_ENHANCED_HUE LITERAL1 +ZIGBEE_COLOR_CAPABILITY_COLOR_LOOP LITERAL1 +ZIGBEE_COLOR_CAPABILITY_X_Y LITERAL1 +ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP LITERAL1 +ZIGBEE_COLOR_MODE_HUE_SATURATION LITERAL1 +ZIGBEE_COLOR_MODE_CURRENT_X_Y LITERAL1 +ZIGBEE_COLOR_MODE_TEMPERATURE LITERAL1 diff --git a/libraries/Zigbee/library.properties b/libraries/Zigbee/library.properties index dab96b82a61..cb671149723 100644 --- a/libraries/Zigbee/library.properties +++ b/libraries/Zigbee/library.properties @@ -1,5 +1,5 @@ name=Zigbee -version=3.3.0 +version=3.3.4 author=P-R-O-C-H-Y maintainer=Jan Procházka sentence=Enables zigbee connection with the ESP32 diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index ab94a163f3e..837c19fa461 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Zigbee library header file for includes of all Zigbee library headers. #pragma once @@ -31,6 +45,7 @@ #include "ep/ZigbeeElectricalMeasurement.h" #include "ep/ZigbeeFlowSensor.h" #include "ep/ZigbeeIlluminanceSensor.h" +#include "ep/ZigbeeMultistate.h" #include "ep/ZigbeeOccupancySensor.h" #include "ep/ZigbeePM25Sensor.h" #include "ep/ZigbeePressureSensor.h" diff --git a/libraries/Zigbee/src/ZigbeeCore.cpp b/libraries/Zigbee/src/ZigbeeCore.cpp index 90b29cf9d0a..9706d732752 100644 --- a/libraries/Zigbee/src/ZigbeeCore.cpp +++ b/libraries/Zigbee/src/ZigbeeCore.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee Core Functions */ #include "ZigbeeCore.h" @@ -32,6 +46,7 @@ ZigbeeCore::ZigbeeCore() { _scan_duration = 3; // default scan duration _rx_on_when_idle = true; _debug = false; + _allow_multi_endpoint_binding = false; _global_default_response_cb = nullptr; // Initialize global callback to nullptr if (!lock) { lock = xSemaphoreCreateBinary(); @@ -378,7 +393,9 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } @@ -408,11 +425,13 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { break; } } - log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); if (!found) { + log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } @@ -515,8 +534,14 @@ void ZigbeeCore::scanNetworks(u_int32_t channel_mask, u_int8_t scan_duration) { log_e("Zigbee stack is not started, cannot scan networks"); return; } + if (_scan_status == ZB_SCAN_RUNNING) { + log_w("Scan already in progress, ignoring new scan request"); + return; + } log_v("Scanning Zigbee networks"); + esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zdo_active_scan_request(channel_mask, scan_duration, scanCompleteCallback); + esp_zb_lock_release(); _scan_status = ZB_SCAN_RUNNING; } @@ -750,6 +775,24 @@ void ZigbeeCore::setNVRAMChannelMask(uint32_t mask) { log_v("Channel mask set to 0x%08x", mask); } +void ZigbeeCore::stop() { + if (started()) { + vTaskSuspend(xTaskGetHandle("Zigbee_main")); + log_v("Zigbee stack stopped"); + _started = false; + } + return; +} + +void ZigbeeCore::start() { + if (!started()) { + vTaskResume(xTaskGetHandle("Zigbee_main")); + log_v("Zigbee stack started"); + _started = true; + } + return; +} + // Function to convert enum value to string const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) { switch (deviceId) { diff --git a/libraries/Zigbee/src/ZigbeeCore.h b/libraries/Zigbee/src/ZigbeeCore.h index df334e1620d..6295832fa03 100644 --- a/libraries/Zigbee/src/ZigbeeCore.h +++ b/libraries/Zigbee/src/ZigbeeCore.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee core class */ #pragma once @@ -103,6 +117,7 @@ class ZigbeeCore { zigbee_scan_result_t *_scan_result; SemaphoreHandle_t lock; bool _debug; + bool _allow_multi_endpoint_binding; // Global default response callback void (*_global_default_response_cb)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster); @@ -124,6 +139,8 @@ class ZigbeeCore { bool begin(zigbee_role_t role = ZIGBEE_END_DEVICE, bool erase_nvs = false); bool begin(esp_zb_cfg_t *role_cfg, bool erase_nvs = false); // bool end(); + void stop(); + void start(); bool started() { return _started; @@ -180,6 +197,13 @@ class ZigbeeCore { return _debug; } + void allowMultiEndpointBinding(bool allow) { + _allow_multi_endpoint_binding = allow; + } + bool allowMultiEndpointBinding() { + return _allow_multi_endpoint_binding; + } + // Set global default response callback void onGlobalDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster)) { _global_default_response_cb = callback; @@ -206,6 +230,8 @@ class ZigbeeCore { } }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ZIGBEE) extern ZigbeeCore Zigbee; +#endif #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 6b63cae0312..742de06835f 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Common Class for Zigbee End Point */ #include "ZigbeeEP.h" @@ -14,6 +28,8 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) { _ep_config.endpoint = 0; _cluster_list = nullptr; _on_identify = nullptr; + _on_ota_state_change = nullptr; + _on_default_response = nullptr; _read_model = NULL; _read_manufacturer = NULL; _time_status = 0; @@ -145,7 +161,8 @@ bool ZigbeeEP::setBatteryVoltage(uint8_t voltage) { bool ZigbeeEP::reportBatteryPercentage() { /* Send report attributes command */ - esp_zb_zcl_report_attr_cmd_t report_attr_cmd = {0}; + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + memset(&report_attr_cmd, 0, sizeof(report_attr_cmd)); report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_POWER_CONFIG_BATTERY_PERCENTAGE_REMAINING_ID; report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; @@ -166,7 +183,8 @@ bool ZigbeeEP::reportBatteryPercentage() { char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer Manufacture Name & Model Identifier */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); if (short_addr != 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -204,7 +222,8 @@ char *ZigbeeEP::readManufacturer(uint8_t endpoint, uint16_t short_addr, esp_zb_i char *ZigbeeEP::readModel(uint8_t endpoint, uint16_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer Manufacture Name & Model Identifier */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); if (short_addr != 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -304,6 +323,12 @@ void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) { } } +void ZigbeeEP::zbOTAState(bool otaActive) { + if (_on_ota_state_change != NULL) { + _on_ota_state_change(otaActive); + } +} + bool ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) { time_t utc_time = 0; // Check if time is set @@ -375,7 +400,8 @@ bool ZigbeeEP::setTimezone(int32_t gmt_offset) { tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer time */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); if (short_addr >= 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -427,7 +453,8 @@ tm ZigbeeEP::getTime(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ie int32_t ZigbeeEP::getTimezone(uint8_t endpoint, int32_t short_addr, esp_zb_ieee_addr_t ieee_addr) { /* Read peer timezone */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); if (short_addr >= 0) { read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; @@ -543,7 +570,8 @@ static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t } void ZigbeeEP::requestOTAUpdate() { - esp_zb_zdo_match_desc_req_param_t req = {0}; + esp_zb_zdo_match_desc_req_param_t req; + memset(&req, 0, sizeof(req)); uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE}; /* Match the OTA server of coordinator */ diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 23217407003..3a7dea19a41 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Common Class for Zigbee End point */ #pragma once @@ -135,8 +149,11 @@ class ZigbeeEP { // list of all handlers function calls, to be override by EPs implementation virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {}; virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; + virtual void + zbWriteAttributeResponse(uint16_t cluster_id, uint16_t attribute_id, esp_zb_zcl_status_t status, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbOTAState(bool otaActive); virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; @@ -160,6 +177,10 @@ class ZigbeeEP { _on_identify = callback; } + void onOTAStateChange(void (*callback)(bool state)) { + _on_ota_state_change = callback; + } + void onDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status)) { _on_default_response = callback; } @@ -170,6 +191,7 @@ class ZigbeeEP { char *_read_manufacturer; char *_read_model; void (*_on_identify)(uint16_t time); + void (*_on_ota_state_change)(bool state); void (*_on_default_response)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status); time_t _read_time; int32_t _read_timezone; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 0986056dcd9..9b574ea7422 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee Common Functions */ #include "ZigbeeCore.h" #include "Arduino.h" @@ -28,6 +42,7 @@ static bool s_tagid_received = false; static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message); static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message); static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); @@ -58,8 +73,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID: ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; - default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; + case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; + case ESP_ZB_CORE_CMD_WRITE_ATTR_RESP_CB_ID: ret = zb_cmd_write_attr_resp_handler((esp_zb_zcl_cmd_write_attr_resp_message_t *)message); break; + default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } return ret; } @@ -156,6 +172,36 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re return ESP_OK; } +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v( + "Write attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr, + message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster + ); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + esp_zb_zcl_write_attr_resp_variable_t *variable = message->variables; + while (variable) { + log_v("Write attribute response: status(%d), cluster(0x%x), attribute(0x%x)", variable->status, message->info.cluster, variable->attribute_id); + if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) { + (*it)->zbWriteAttributeResponse( + message->info.cluster, variable->attribute_id, variable->status, message->info.src_endpoint, message->info.src_address + ); + } + variable = variable->next; + } + } + } + return ESP_OK; +} + static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message) { if (!message) { log_e("Empty message"); @@ -292,6 +338,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu switch (message->upgrade_status) { case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_START: log_i("Zigbee - OTA upgrade start"); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(true); // Notify that OTA is active + } start_time = esp_timer_get_time(); s_ota_partition = esp_ota_get_next_update_partition(NULL); assert(s_ota_partition); @@ -302,6 +351,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu #endif if (ret != ESP_OK) { log_e("Zigbee - Failed to begin OTA partition, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } break; @@ -315,6 +367,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu ret = esp_element_ota_data(total_size, message->payload, message->payload_size, &payload, &payload_size); if (ret != ESP_OK) { log_e("Zigbee - Failed to element OTA data, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } #if CONFIG_ZB_DELTA_OTA @@ -324,6 +379,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu #endif if (ret != ESP_OK) { log_e("Zigbee - Failed to write OTA data to partition, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } } @@ -343,6 +401,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu message->ota_header.file_version, message->ota_header.manufacturer_code, message->ota_header.image_type, message->ota_header.image_size, (esp_timer_get_time() - start_time) / 1000 ); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } #if CONFIG_ZB_DELTA_OTA ret = esp_delta_ota_end(s_ota_handle); #else diff --git a/libraries/Zigbee/src/ZigbeeTypes.h b/libraries/Zigbee/src/ZigbeeTypes.h index 5025f90db7c..5b5a34fcb5b 100644 --- a/libraries/Zigbee/src/ZigbeeTypes.h +++ b/libraries/Zigbee/src/ZigbeeTypes.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #pragma once #include "esp_zigbee_core.h" diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp index 309739d54c9..6955f0e4b20 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp @@ -1,9 +1,24 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeAnalog.h" #if CONFIG_ZB_ENABLED #include ZigbeeAnalog::ZigbeeAnalog(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; + _on_analog_output_change = nullptr; //Create basic analog sensor clusters without configuration _cluster_list = esp_zb_zcl_cluster_list_create(); diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.h b/libraries/Zigbee/src/ep/ZigbeeAnalog.h index 5218a0b7d60..38a267ad27c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.h +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Analog sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeBinary.cpp b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp index aa37ceb3020..b46cbae0b00 100644 --- a/libraries/Zigbee/src/ep/ZigbeeBinary.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp @@ -1,8 +1,23 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeBinary.h" #if CONFIG_ZB_ENABLED ZigbeeBinary::ZigbeeBinary(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; + _on_binary_output_change = nullptr; //Create basic binary sensor clusters without configuration _cluster_list = esp_zb_zcl_cluster_list_create(); @@ -41,6 +56,36 @@ bool ZigbeeBinary::addBinaryInput() { return true; } +bool ZigbeeBinary::addBinaryOutput() { + esp_zb_attribute_list_t *esp_zb_binary_output_cluster = esp_zb_binary_output_cluster_create(NULL); + + // Create default description for Binary Output + char default_description[] = "\x0D" + "Binary Output"; + uint32_t application_type = 0x00000000 | (0x04 << 24); // Group ID 0x04 + + esp_err_t ret = + esp_zb_binary_output_cluster_add_attr(esp_zb_binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_binary_output_cluster_add_attr(esp_zb_binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_cluster_list_add_binary_output_cluster(_cluster_list, esp_zb_binary_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Binary Output cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + _binary_clusters |= BINARY_OUTPUT; + return true; +} + // Check Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types for application type values bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) { if (!(_binary_clusters & BINARY_INPUT)) { @@ -61,6 +106,26 @@ bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) { return true; } +// Check Zigbee Cluster Specification 3.14.11.19.5 Binary Outputs (BO) Types for application type values +bool ZigbeeBinary::setBinaryOutputApplication(uint32_t application_type) { + if (!(_binary_clusters & BINARY_OUTPUT)) { + log_e("Binary Output cluster not added"); + return false; + } + + // Add the Binary Output group ID (0x04) to the application type + uint32_t application_type_value = (0x04 << 24) | application_type; + + esp_zb_attribute_list_t *binary_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Binary Output application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeBinary::setBinaryInput(bool input) { esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; if (!(_binary_clusters & BINARY_INPUT)) { @@ -141,4 +206,107 @@ bool ZigbeeBinary::setBinaryInputDescription(const char *description) { return true; } +bool ZigbeeBinary::setBinaryOutputDescription(const char *description) { + if (!(_binary_clusters & BINARY_OUTPUT)) { + log_e("Binary Output cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the binary output cluster + esp_zb_attribute_list_t *binary_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (binary_output_cluster == nullptr) { + log_e("Failed to get binary output cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +//set attribute method -> method overridden in child class +void ZigbeeBinary::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { + _output_state = *(bool *)message->attribute.data.value; + binaryOutputChanged(); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for Binary Output", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Binary endpoint", message->info.cluster); + } +} + +void ZigbeeBinary::binaryOutputChanged() { + if (_on_binary_output_change) { + _on_binary_output_change(_output_state); + } else { + log_w("No callback function set for binary output change"); + } +} + +bool ZigbeeBinary::setBinaryOutput(bool output) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _output_state = output; + binaryOutputChanged(); + + log_v("Updating binary output to %d", output); + /* Update binary output */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID, &_output_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set binary output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeBinary::reportBinaryOutput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Binary Output report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Binary Output report sent"); + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeBinary.h b/libraries/Zigbee/src/ep/ZigbeeBinary.h index 5a543604970..c0ca3c8b92f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeBinary.h +++ b/libraries/Zigbee/src/ep/ZigbeeBinary.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Binary sensor endpoint inherited from common EP class */ #pragma once @@ -38,10 +52,26 @@ enum zigbee_binary_clusters { #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 // Type 0x01, Index 0x0008 #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF +// HVAC application types for Binary Output (more can be found in Zigbee Cluster Specification 3.14.11.19.5 Binary Outputs (BO) Types) +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_BOILER 0x00000003 // Type 0x00, Index 0x0003 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_CHILLER 0x0000000D // Type 0x00, Index 0x000D +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN 0x00000022 // Type 0x00, Index 0x0022 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HEATING_VALVE 0x0000002C // Type 0x00, Index 0x002C +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER 0x00000033 // Type 0x00, Index 0x0033 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_PREHEAT 0x00000034 // Type 0x00, Index 0x0034 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF // Type 0x00, Index 0xFFFF + +// Security application types for Binary Output +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ARM_DISARM_COMMAND 0x01000000 // Type 0x01, Index 0x0000 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OCCUPANCY_CONTROL 0x01000001 // Type 0x01, Index 0x0001 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ENABLE_CONTROL 0x01000002 // Type 0x01, Index 0x0002 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ACCESS_CONTROL 0x01000003 // Type 0x01, Index 0x0003 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF + typedef struct zigbee_binary_cfg_s { esp_zb_basic_cluster_cfg_t basic_cfg; esp_zb_identify_cluster_cfg_t identify_cfg; - // esp_zb_binary_output_cluster_cfg_t binary_output_cfg; + esp_zb_binary_output_cluster_cfg_t binary_output_cfg; esp_zb_binary_input_cluster_cfg_t binary_input_cfg; } zigbee_binary_cfg_t; @@ -52,33 +82,41 @@ class ZigbeeBinary : public ZigbeeEP { // Add binary cluster bool addBinaryInput(); - // bool addBinaryOutput(); + bool addBinaryOutput(); // Set the application type and description for the binary input bool setBinaryInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_input.h for application type values bool setBinaryInputDescription(const char *description); // Set the application type and description for the binary output - // bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values - // bool setBinaryOutputDescription(const char *description); + bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values + bool setBinaryOutputDescription(const char *description); // Use to set a cb function to be called on binary output change - // void onBinaryOutputChange(void (*callback)(bool binary_output)) { - // _on_binary_output_change = callback; - // } + void onBinaryOutputChange(void (*callback)(bool binary_output)) { + _on_binary_output_change = callback; + } - // Set the binary input value + // Set the binary input/output value bool setBinaryInput(bool input); + bool setBinaryOutput(bool output); + + // Get the Binary Output value + bool getBinaryOutput() { + return _output_state; + } - // Report Binary Input value + // Report Binary Input/Output value bool reportBinaryInput(); + bool reportBinaryOutput(); private: - // void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; - // void (*_on_binary_output_change)(bool); - // void binaryOutputChanged(bool binary_output); + void (*_on_binary_output_change)(bool); + void binaryOutputChanged(); + bool _output_state; uint8_t _binary_clusters; }; diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index 2b8271f09a9..225bb620f0b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeCarbonDioxideSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h index e0a6de48648..bd64e50e51e 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp index 3611c232c20..a31cf174c4e 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include "ZigbeeColorDimmableLight.h" #if CONFIG_ZB_ENABLED @@ -12,9 +26,19 @@ ZigbeeColorDimmableLight::ZigbeeColorDimmableLight(uint8_t endpoint) : ZigbeeEP( uint8_t hue = 0; uint8_t saturation = 0; + // Add support for Color Temperature and Hue Saturation attributes + uint16_t color_temperature = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_DEF_VALUE; + uint16_t min_temp = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MIN_MIREDS_DEFAULT_VALUE; + uint16_t max_temp = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MAX_MIREDS_DEFAULT_VALUE; + esp_zb_attribute_list_t *color_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hue); esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &saturation); + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMPERATURE_ID, &color_temperature); + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MIN_MIREDS_ID, &min_temp); + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MAX_MIREDS_ID, &max_temp); + uint8_t color_mode = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_MODE_DEFAULT_VALUE; + esp_zb_color_control_cluster_add_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_MODE_ID, &color_mode); _ep_config = { .endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_COLOR_DIMMABLE_LIGHT_DEVICE_ID, .app_device_version = 0 @@ -24,6 +48,15 @@ ZigbeeColorDimmableLight::ZigbeeColorDimmableLight(uint8_t endpoint) : ZigbeeEP( _current_state = false; _current_level = 255; _current_color = {255, 255, 255}; + _current_hsv = {0, 0, 255}; + _current_color_temperature = ESP_ZB_ZCL_COLOR_CONTROL_COLOR_TEMPERATURE_DEF_VALUE; + _current_color_mode = ZIGBEE_COLOR_MODE_CURRENT_X_Y; //default XY color mode + _color_capabilities = ZIGBEE_COLOR_CAPABILITY_X_Y; //default XY color supported only + + // Initialize callbacks to nullptr + _on_light_change_rgb = nullptr; + _on_light_change_hsv = nullptr; + _on_light_change_temp = nullptr; } uint16_t ZigbeeColorDimmableLight::getCurrentColorX() { @@ -48,12 +81,19 @@ uint8_t ZigbeeColorDimmableLight::getCurrentColorHue() { } uint8_t ZigbeeColorDimmableLight::getCurrentColorSaturation() { - return (*(uint16_t *)esp_zb_zcl_get_attribute( + return (*(uint8_t *)esp_zb_zcl_get_attribute( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID ) ->data_p); } +uint16_t ZigbeeColorDimmableLight::getCurrentColorTemperature() { + return (*(uint16_t *)esp_zb_zcl_get_attribute( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMPERATURE_ID + ) + ->data_p); +} + //set attribute method -> method overridden in child class void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { //check the data and call right method @@ -61,7 +101,7 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { if (_current_state != *(bool *)message->attribute.data.value) { _current_state = *(bool *)message->attribute.data.value; - lightChanged(); + lightChangedByMode(); } return; } else { @@ -71,37 +111,117 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me if (message->attribute.id == ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { if (_current_level != *(uint8_t *)message->attribute.data.value) { _current_level = *(uint8_t *)message->attribute.data.value; - lightChanged(); + // Update HSV value if in HSV mode + if (_current_color_mode == ZIGBEE_COLOR_MODE_HUE_SATURATION) { + _current_hsv.v = _current_level; + } + lightChangedByMode(); } return; } else { log_w("Received message ignored. Attribute ID: %d not supported for Level Control", message->attribute.id); } } else if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL) { - if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_MODE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_8BIT_ENUM) { + uint8_t new_color_mode = (*(uint8_t *)message->attribute.data.value); + if (new_color_mode > ZIGBEE_COLOR_MODE_TEMPERATURE) { + log_w("Invalid color mode received: %d", new_color_mode); + return; + } + + // Validate that the requested color mode is supported by capabilities + if (!isColorModeSupported(new_color_mode)) { + log_w("Color mode %d not supported by current capabilities: 0x%04x", new_color_mode, _color_capabilities); + return; + } + + _current_color_mode = new_color_mode; + log_v("Color mode changed to: %d", _current_color_mode); + // Don't call setLightColorMode() here - the attribute was already set externally + // Just update our internal state + return; + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + // Validate XY capability + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_X_Y)) { + log_w("XY color capability not enabled, but XY attribute received. Current capabilities: 0x%04x", _color_capabilities); + return; + } uint16_t light_color_x = (*(uint16_t *)message->attribute.data.value); uint16_t light_color_y = getCurrentColorY(); - //calculate RGB from XY and call setColor() + // Update color mode to XY if not already + if (_current_color_mode != ZIGBEE_COLOR_MODE_CURRENT_X_Y) { + setLightColorMode(ZIGBEE_COLOR_MODE_CURRENT_X_Y); + } + //calculate RGB from XY and call RGB callback _current_color = espXYToRgbColor(255, light_color_x, light_color_y, false); - lightChanged(); + lightChangedRgb(); return; } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + // Validate XY capability + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_X_Y)) { + log_w("XY color capability not enabled, but XY attribute received. Current capabilities: 0x%04x", _color_capabilities); + return; + } uint16_t light_color_x = getCurrentColorX(); uint16_t light_color_y = (*(uint16_t *)message->attribute.data.value); - //calculate RGB from XY and call setColor() + // Update color mode to XY if not already + if (_current_color_mode != ZIGBEE_COLOR_MODE_CURRENT_X_Y) { + setLightColorMode(ZIGBEE_COLOR_MODE_CURRENT_X_Y); + } + //calculate RGB from XY and call RGB callback _current_color = espXYToRgbColor(255, light_color_x, light_color_y, false); - lightChanged(); + lightChangedRgb(); return; } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + // Validate Hue/Saturation capability + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION)) { + log_w("Hue/Saturation color capability not enabled, but Hue attribute received. Current capabilities: 0x%04x", _color_capabilities); + return; + } uint8_t light_color_hue = (*(uint8_t *)message->attribute.data.value); - _current_color = espHsvToRgbColor(light_color_hue, getCurrentColorSaturation(), 255); - lightChanged(); + // Update color mode to HS if not already + if (_current_color_mode != ZIGBEE_COLOR_MODE_HUE_SATURATION) { + setLightColorMode(ZIGBEE_COLOR_MODE_HUE_SATURATION); + } + // Store HSV values and call HSV callback (don't convert to RGB) + _current_hsv.h = light_color_hue; + _current_hsv.s = getCurrentColorSaturation(); + _current_hsv.v = _current_level; // Use level as value + lightChangedHsv(); return; } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + // Validate Hue/Saturation capability + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION)) { + log_w("Hue/Saturation color capability not enabled, but Saturation attribute received. Current capabilities: 0x%04x", _color_capabilities); + return; + } uint8_t light_color_saturation = (*(uint8_t *)message->attribute.data.value); - _current_color = espHsvToRgbColor(getCurrentColorHue(), light_color_saturation, 255); - lightChanged(); + // Update color mode to HS if not already + if (_current_color_mode != ZIGBEE_COLOR_MODE_HUE_SATURATION) { + setLightColorMode(ZIGBEE_COLOR_MODE_HUE_SATURATION); + } + // Store HSV values and call HSV callback (don't convert to RGB) + _current_hsv.h = getCurrentColorHue(); + _current_hsv.s = light_color_saturation; + _current_hsv.v = _current_level; // Use level as value + lightChangedHsv(); + return; + } else if (message->attribute.id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMPERATURE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + // Validate Color Temperature capability + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP)) { + log_w("Color temperature capability not enabled, but Temperature attribute received. Current capabilities: 0x%04x", _color_capabilities); + return; + } + uint16_t light_color_temp = (*(uint16_t *)message->attribute.data.value); + // Update color mode to TEMP if not already + if (_current_color_mode != ZIGBEE_COLOR_MODE_TEMPERATURE) { + setLightColorMode(ZIGBEE_COLOR_MODE_TEMPERATURE); + } + if (_current_color_temperature != light_color_temp) { + _current_color_temperature = light_color_temp; + lightChangedTemp(); + } return; } else { log_w("Received message ignored. Attribute ID: %d not supported for Color Control", message->attribute.id); @@ -111,24 +231,58 @@ void ZigbeeColorDimmableLight::zbAttributeSet(const esp_zb_zcl_set_attr_value_me } } -void ZigbeeColorDimmableLight::lightChanged() { - if (_on_light_change) { - _on_light_change(_current_state, _current_color.r, _current_color.g, _current_color.b, _current_level); +void ZigbeeColorDimmableLight::lightChangedRgb() { + if (_on_light_change_rgb) { + _on_light_change_rgb(_current_state, _current_color.r, _current_color.g, _current_color.b, _current_level); + } +} + +void ZigbeeColorDimmableLight::lightChangedHsv() { + if (_on_light_change_hsv) { + _on_light_change_hsv(_current_state, _current_hsv.h, _current_hsv.s, _current_hsv.v); + } +} + +void ZigbeeColorDimmableLight::lightChangedTemp() { + if (_on_light_change_temp) { + _on_light_change_temp(_current_state, _current_level, _current_color_temperature); + } +} + +void ZigbeeColorDimmableLight::lightChangedByMode() { + // Call the appropriate callback based on current color mode + switch (_current_color_mode) { + case ZIGBEE_COLOR_MODE_CURRENT_X_Y: lightChangedRgb(); break; + case ZIGBEE_COLOR_MODE_HUE_SATURATION: lightChangedHsv(); break; + case ZIGBEE_COLOR_MODE_TEMPERATURE: lightChangedTemp(); break; + default: log_e("Unknown color mode: %d", _current_color_mode); break; } } bool ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue) { + // Check if XY color capability is enabled + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_X_Y)) { + log_e("XY color capability not enabled. Current capabilities: 0x%04x", _color_capabilities); + return false; + } + if (!setLightColorMode(ZIGBEE_COLOR_MODE_CURRENT_X_Y)) { + log_e("Failed to set light color mode: %d", ZIGBEE_COLOR_MODE_CURRENT_X_Y); + return false; + } esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; - //Update all attributes + // Update all attributes _current_state = state; _current_level = level; _current_color = {red, green, blue}; - lightChanged(); + lightChangedRgb(); espXyColor_t xy_color = espRgbColorToXYColor(_current_color); espHsvColor_t hsv_color = espRgbColorToHsvColor(_current_color); - uint8_t hue = std::min((uint8_t)hsv_color.h, (uint8_t)254); // Clamp to 0-254 - uint8_t saturation = std::min((uint8_t)hsv_color.s, (uint8_t)254); // Clamp to 0-254 + // Clamp hue and saturation to valid Zigbee range (0-254, where 254 = 0xFE is max per ZCL spec) + uint8_t hue = std::min(std::max((uint8_t)hsv_color.h, (uint8_t)0), (uint8_t)254); + uint8_t saturation = std::min(std::max((uint8_t)hsv_color.s, (uint8_t)0), (uint8_t)254); + // Update HSV state + _current_hsv = hsv_color; log_v("Updating light state: %d, level: %d, color: %d, %d, %d", state, level, red, green, blue); /* Update light clusters */ @@ -165,7 +319,7 @@ bool ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, log_e("Failed to set light y color: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); goto unlock_and_return; } - //set hue + //set hue (for compatibility) ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hue, false ); @@ -173,7 +327,7 @@ bool ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, log_e("Failed to set light hue: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); goto unlock_and_return; } - //set saturation + //set saturation (for compatibility) ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &saturation, false ); @@ -187,11 +341,52 @@ bool ZigbeeColorDimmableLight::setLight(bool state, uint8_t level, uint8_t red, } bool ZigbeeColorDimmableLight::setLightState(bool state) { - return setLight(state, _current_level, _current_color.r, _current_color.g, _current_color.b); + if (_current_state == state) { + return true; // No change needed + } + + _current_state = state; + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_ON_OFF, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID, &_current_state, false + ); + esp_zb_lock_release(); + + if (ret == ESP_ZB_ZCL_STATUS_SUCCESS) { + lightChangedByMode(); // Call appropriate callback based on current color mode + } else { + log_e("Failed to set light state: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + } + + return ret == ESP_ZB_ZCL_STATUS_SUCCESS; } bool ZigbeeColorDimmableLight::setLightLevel(uint8_t level) { - return setLight(_current_state, level, _current_color.r, _current_color.g, _current_color.b); + if (_current_level == level) { + return true; // No change needed + } + + _current_level = level; + // Update HSV value if in HSV mode + if (_current_color_mode == ZIGBEE_COLOR_MODE_HUE_SATURATION) { + _current_hsv.v = level; + } + + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, &_current_level, false + ); + esp_zb_lock_release(); + + if (ret == ESP_ZB_ZCL_STATUS_SUCCESS) { + lightChangedByMode(); // Call appropriate callback based on current color mode + } else { + log_e("Failed to set light level: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + } + + return ret == ESP_ZB_ZCL_STATUS_SUCCESS; } bool ZigbeeColorDimmableLight::setLightColor(uint8_t red, uint8_t green, uint8_t blue) { @@ -203,8 +398,172 @@ bool ZigbeeColorDimmableLight::setLightColor(espRgbColor_t rgb_color) { } bool ZigbeeColorDimmableLight::setLightColor(espHsvColor_t hsv_color) { - espRgbColor_t rgb_color = espHsvColorToRgbColor(hsv_color); - return setLight(_current_state, _current_level, rgb_color.r, rgb_color.g, rgb_color.b); + // Check if Hue/Saturation color capability is enabled + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION)) { + log_e("Hue/Saturation color capability not enabled. Current capabilities: 0x%04x", _color_capabilities); + return false; + } + + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + if (!setLightColorMode(ZIGBEE_COLOR_MODE_HUE_SATURATION)) { + log_e("Failed to set light color mode: %d", ZIGBEE_COLOR_MODE_HUE_SATURATION); + return false; + } + // Update HSV state and level from value component + _current_hsv = hsv_color; + _current_level = hsv_color.v; // Use HSV value component to update brightness level + lightChangedHsv(); + + // Clamp hue and saturation to valid Zigbee range (0-254, where 254 = 0xFE is max per ZCL spec) + uint8_t hue = std::clamp((uint8_t)hsv_color.h, (uint8_t)0, (uint8_t)254); + uint8_t saturation = std::clamp((uint8_t)hsv_color.s, (uint8_t)0, (uint8_t)254); + + log_v("Updating light HSV: H=%d, S=%d, V=%d (level=%d)", hue, saturation, hsv_color.v, _current_level); + /* Update light clusters */ + esp_zb_lock_acquire(portMAX_DELAY); + //set level (brightness from HSV value component) + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID, &_current_level, false + ); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set light level: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + goto unlock_and_return; + } + //set hue + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, &hue, false + ); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set light hue: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + goto unlock_and_return; + } + //set saturation + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID, &saturation, false + ); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set light saturation: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + goto unlock_and_return; + } +unlock_and_return: + esp_zb_lock_release(); + return ret == ESP_ZB_ZCL_STATUS_SUCCESS; +} + +bool ZigbeeColorDimmableLight::setLightColorTemperature(uint16_t color_temperature) { + // Check if Color Temperature capability is enabled + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP)) { + log_e("Color temperature capability not enabled. Current capabilities: 0x%04x", _color_capabilities); + return false; + } + if (!setLightColorMode(ZIGBEE_COLOR_MODE_TEMPERATURE)) { + log_e("Failed to set light color mode: %d", ZIGBEE_COLOR_MODE_TEMPERATURE); + return false; + } + + _current_color_temperature = color_temperature; + lightChangedTemp(); + + log_v("Updating light color temperature: %d", color_temperature); + + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + /* Update light clusters */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMPERATURE_ID, + &_current_color_temperature, false + ); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set light color temperature: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + goto unlock_and_return; + } +unlock_and_return: + esp_zb_lock_release(); + return ret == ESP_ZB_ZCL_STATUS_SUCCESS; +} + +bool ZigbeeColorDimmableLight::isColorModeSupported(uint8_t color_mode) { + switch (color_mode) { + case ZIGBEE_COLOR_MODE_CURRENT_X_Y: return (_color_capabilities & ZIGBEE_COLOR_CAPABILITY_X_Y) != 0; + case ZIGBEE_COLOR_MODE_HUE_SATURATION: return (_color_capabilities & ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION) != 0; + case ZIGBEE_COLOR_MODE_TEMPERATURE: return (_color_capabilities & ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP) != 0; + default: return false; + } +} + +bool ZigbeeColorDimmableLight::setLightColorMode(uint8_t color_mode) { + if (color_mode > ZIGBEE_COLOR_MODE_TEMPERATURE) { + log_e("Invalid color mode: %d", color_mode); + return false; + } + + // Check if the requested color mode is supported by capabilities + if (!isColorModeSupported(color_mode)) { + log_e("Color mode %d not supported by current capabilities: 0x%04x", color_mode, _color_capabilities); + return false; + } + + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + log_v("Setting color mode: %d", color_mode); + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_MODE_ID, &color_mode, false + ); + esp_zb_lock_release(); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set light color mode: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + _current_color_mode = color_mode; + return true; +} + +bool ZigbeeColorDimmableLight::setLightColorCapabilities(uint16_t capabilities) { + esp_zb_attribute_list_t *color_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (!color_cluster) { + log_e("Color control cluster not found"); + return false; + } + + // Validate capabilities (max value is 0x001f per ZCL spec) + if (capabilities > 0x001f) { + log_e("Invalid color capabilities value: 0x%04x (max: 0x001f)", capabilities); + return false; + } + + _color_capabilities = capabilities; + + esp_err_t ret = esp_zb_cluster_update_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_CAPABILITIES_ID, &_color_capabilities); + if (ret != ESP_OK) { + log_e("Failed to set color capabilities: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + log_v("Color capabilities set to: 0x%04x", _color_capabilities); + return true; +} + +bool ZigbeeColorDimmableLight::setLightColorTemperatureRange(uint16_t min_temp, uint16_t max_temp) { + esp_zb_attribute_list_t *color_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (!color_cluster) { + log_e("Color control cluster not found"); + return false; + } + if (!(_color_capabilities & ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP)) { + log_e("Color temperature capability not enabled. Current capabilities: 0x%04x", _color_capabilities); + return false; + } + esp_err_t ret = esp_zb_cluster_update_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MIN_MIREDS_ID, &min_temp); + if (ret != ESP_OK) { + log_e("Failed to set min value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_update_attr(color_cluster, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_COLOR_TEMP_PHYSICAL_MAX_MIREDS_ID, &max_temp); + if (ret != ESP_OK) { + log_e("Failed to set max value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; } #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h index 6681f213ad0..92fbb913f2c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once @@ -50,16 +64,51 @@ }, \ } +// Color capabilities bit flags (matching ZCL spec) - can be combined with bitwise OR +static constexpr uint16_t ZIGBEE_COLOR_CAPABILITY_HUE_SATURATION = (1 << 0); // Bit 0: Hue/saturation supported +static constexpr uint16_t ZIGBEE_COLOR_CAPABILITY_ENHANCED_HUE = (1 << 1); // Bit 1: Enhanced hue supported +static constexpr uint16_t ZIGBEE_COLOR_CAPABILITY_COLOR_LOOP = (1 << 2); // Bit 2: Color loop supported +static constexpr uint16_t ZIGBEE_COLOR_CAPABILITY_X_Y = (1 << 3); // Bit 3: X/Y supported +static constexpr uint16_t ZIGBEE_COLOR_CAPABILITY_COLOR_TEMP = (1 << 4); // Bit 4: Color temperature supported + +// Color mode enum values (matching ZCL spec) +enum ZigbeeColorMode { + ZIGBEE_COLOR_MODE_HUE_SATURATION = 0x00, // CurrentHue and CurrentSaturation + ZIGBEE_COLOR_MODE_CURRENT_X_Y = 0x01, // CurrentX and CurrentY // codespell:ignore currenty + ZIGBEE_COLOR_MODE_TEMPERATURE = 0x02, // ColorTemperature +}; + +// Callback function type definitions for better readability and type safety +// RGB callback: (state, red, green, blue, level) +typedef void (*ZigbeeColorLightRgbCallback)(bool state, uint8_t red, uint8_t green, uint8_t blue, uint8_t level); +// HSV callback: (state, hue, saturation, value) - value represents brightness (0-255) +typedef void (*ZigbeeColorLightHsvCallback)(bool state, uint8_t hue, uint8_t saturation, uint8_t value); +// Temperature callback: (state, level, color_temperature_in_mireds) +typedef void (*ZigbeeColorLightTempCallback)(bool state, uint8_t level, uint16_t color_temperature); + class ZigbeeColorDimmableLight : public ZigbeeEP { public: ZigbeeColorDimmableLight(uint8_t endpoint); ~ZigbeeColorDimmableLight() {} - void onLightChange(void (*callback)(bool, uint8_t, uint8_t, uint8_t, uint8_t)) { - _on_light_change = callback; + // Must be called before starting Zigbee, by default XY are selected as color mode + bool setLightColorCapabilities(uint16_t capabilities); + + [[deprecated("Use onLightChangeRgb() instead. This will be removed in a future major version.")]] + void onLightChange(ZigbeeColorLightRgbCallback callback) { + _on_light_change_rgb = callback; + } + void onLightChangeRgb(ZigbeeColorLightRgbCallback callback) { + _on_light_change_rgb = callback; + } + void onLightChangeHsv(ZigbeeColorLightHsvCallback callback) { + _on_light_change_hsv = callback; + } + void onLightChangeTemp(ZigbeeColorLightTempCallback callback) { + _on_light_change_temp = callback; } void restoreLight() { - lightChanged(); + lightChangedByMode(); } bool setLightState(bool state); @@ -67,7 +116,9 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { bool setLightColor(uint8_t red, uint8_t green, uint8_t blue); bool setLightColor(espRgbColor_t rgb_color); bool setLightColor(espHsvColor_t hsv_color); + bool setLightColorTemperature(uint16_t color_temperature); bool setLight(bool state, uint8_t level, uint8_t red, uint8_t green, uint8_t blue); + bool setLightColorTemperatureRange(uint16_t min_temp, uint16_t max_temp); bool getLightState() { return _current_state; @@ -87,22 +138,53 @@ class ZigbeeColorDimmableLight : public ZigbeeEP { uint8_t getLightBlue() { return _current_color.b; } + uint16_t getLightColorTemperature() { + return _current_color_temperature; + } + uint8_t getLightColorMode() { + return _current_color_mode; + } + uint8_t getLightColorHue() { + return _current_hsv.h; + } + uint8_t getLightColorSaturation() { + return _current_hsv.s; + } + uint16_t getLightColorCapabilities() { + return _color_capabilities; + } private: void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + bool setLightColorMode(uint8_t color_mode); + bool isColorModeSupported(uint8_t color_mode); uint16_t getCurrentColorX(); uint16_t getCurrentColorY(); uint8_t getCurrentColorHue(); uint8_t getCurrentColorSaturation(); + uint16_t getCurrentColorTemperature(); - void lightChanged(); - //callback function to be called on light change (State, R, G, B, Level) - void (*_on_light_change)(bool, uint8_t, uint8_t, uint8_t, uint8_t); + void lightChangedRgb(); + void lightChangedHsv(); + void lightChangedTemp(); + void lightChangedByMode(); + + //callback function to be called on light change for RGB (State, R, G, B, Level) + ZigbeeColorLightRgbCallback _on_light_change_rgb; + //callback function to be called on light change for HSV (State, H, S, V, Level) + ZigbeeColorLightHsvCallback _on_light_change_hsv; + //callback function to be called on light change for TEMP (State, Level, Temperature) + ZigbeeColorLightTempCallback _on_light_change_temp; bool _current_state; uint8_t _current_level; espRgbColor_t _current_color; + espHsvColor_t _current_hsv; + uint16_t _current_color_temperature; + uint8_t _current_color_mode; + + uint16_t _color_capabilities; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp index f795ed1a3c8..18465104dfa 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeColorDimmerSwitch.h" #if CONFIG_ZB_ENABLED @@ -9,6 +23,16 @@ ZigbeeColorDimmerSwitch::ZigbeeColorDimmerSwitch(uint8_t endpoint) : ZigbeeEP(en _instance = this; // Set the static pointer to this instance _device = nullptr; // Initialize light pointer to null + _light_color_rgb = {0, 0, 0}; + _light_color_hsv = {0, 0, 255}; + _light_color_xy = {0, 0}; + _on_light_state_change = nullptr; + _on_light_state_change_with_source = nullptr; + _on_light_level_change = nullptr; + _on_light_level_change_with_source = nullptr; + _on_light_color_change = nullptr; + _on_light_color_change_with_source = nullptr; + esp_zb_color_dimmable_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_COLOR_DIMMABLE_SWITCH_CONFIG(); _cluster_list = esp_zb_color_dimmable_switch_clusters_create(&switch_cfg); @@ -53,7 +77,8 @@ void ZigbeeColorDimmerSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t ad ZigbeeColorDimmerSwitch *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_d("Found light endpoint"); - esp_zb_zdo_bind_req_param_t bind_req = {0}; + esp_zb_zdo_bind_req_param_t bind_req; + memset(&bind_req, 0, sizeof(bind_req)); zb_device_params_t *light = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); light->endpoint = endpoint; light->short_addr = addr; @@ -97,7 +122,8 @@ void ZigbeeColorDimmerSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cm // Methods to control the light void ZigbeeColorDimmerSwitch::lightToggle() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID; @@ -112,7 +138,8 @@ void ZigbeeColorDimmerSwitch::lightToggle() { void ZigbeeColorDimmerSwitch::lightToggle(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -128,7 +155,8 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -145,7 +173,8 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -165,7 +194,8 @@ void ZigbeeColorDimmerSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t i void ZigbeeColorDimmerSwitch::lightOn() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_ON_ID; @@ -180,7 +210,8 @@ void ZigbeeColorDimmerSwitch::lightOn() { void ZigbeeColorDimmerSwitch::lightOn(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -196,7 +227,8 @@ void ZigbeeColorDimmerSwitch::lightOn(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -213,7 +245,8 @@ void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -233,7 +266,8 @@ void ZigbeeColorDimmerSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_ void ZigbeeColorDimmerSwitch::lightOff() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_OFF_ID; @@ -248,7 +282,8 @@ void ZigbeeColorDimmerSwitch::lightOff() { void ZigbeeColorDimmerSwitch::lightOff(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -264,7 +299,8 @@ void ZigbeeColorDimmerSwitch::lightOff(uint16_t group_addr) { void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -281,7 +317,8 @@ void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -301,7 +338,8 @@ void ZigbeeColorDimmerSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee void ZigbeeColorDimmerSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) { if (_is_bound) { - esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.effect_id = effect_id; @@ -317,7 +355,8 @@ void ZigbeeColorDimmerSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effe void ZigbeeColorDimmerSwitch::lightOnWithSceneRecall() { if (_is_bound) { - esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; log_v("Sending 'light on with scene recall' command"); @@ -331,7 +370,8 @@ void ZigbeeColorDimmerSwitch::lightOnWithSceneRecall() { void ZigbeeColorDimmerSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, uint16_t time_off) { if (_is_bound) { - esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_control = on_off_control; //TODO: Test how it works, then maybe change API @@ -348,7 +388,8 @@ void ZigbeeColorDimmerSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16 void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; + esp_zb_zcl_move_to_level_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.level = level; @@ -364,7 +405,8 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level) { void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; + esp_zb_zcl_move_to_level_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -381,7 +423,8 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint16_t group_addr) void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; + esp_zb_zcl_move_to_level_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -399,7 +442,8 @@ void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, uin void ZigbeeColorDimmerSwitch::setLightLevel(uint8_t level, uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_move_to_level_cmd_t cmd_req = {0}; + esp_zb_zcl_move_to_level_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -422,7 +466,8 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.color_x = xy_color.x; @@ -441,7 +486,8 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -461,7 +507,8 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -482,7 +529,8 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t if (_is_bound) { espXyColor_t xy_color = espRgbToXYColor(red, green, blue); - esp_zb_zcl_color_move_to_color_cmd_t cmd_req = {0}; + esp_zb_zcl_color_move_to_color_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -502,4 +550,356 @@ void ZigbeeColorDimmerSwitch::setLightColor(uint8_t red, uint8_t green, uint8_t } } +void ZigbeeColorDimmerSwitch::getLightState() { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightState(uint16_t group_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightState(uint8_t endpoint, uint16_t short_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightState(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightLevel() { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightLevel(uint16_t group_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightLevel(uint8_t endpoint, uint16_t short_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightLevel(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColor() { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColor(uint16_t group_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColor(uint8_t endpoint, uint16_t short_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColor(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColorHS() { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColorHS(uint16_t group_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColorHS(uint8_t endpoint, uint16_t short_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::getLightColorHS(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL; + read_req.attr_number = 2; + uint16_t attr_id[] = {ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID, ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID}; + read_req.attr_field = attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeColorDimmerSwitch::zbAttributeRead( + uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address +) { + if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) { + if (attribute->id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { + bool light_state = attribute->data.value ? *(bool *)attribute->data.value : false; + if (_on_light_state_change) { + _on_light_state_change(light_state); + } + if (_on_light_state_change_with_source) { + _on_light_state_change_with_source(light_state, src_endpoint, src_address); + } + } + } + if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL) { + if (attribute->id == ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + uint8_t light_level = attribute->data.value ? *(uint8_t *)attribute->data.value : 0; + if (_on_light_level_change) { + _on_light_level_change(light_level); + } + if (_on_light_level_change_with_source) { + _on_light_level_change_with_source(light_level, src_endpoint, src_address); + } + } + } + if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_COLOR_CONTROL) { + static bool x_received = false; + static bool y_received = false; + static bool h_received = false; + static bool s_received = false; + + if (attribute->id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_X_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _light_color_xy.x = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + x_received = true; + } + if (attribute->id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_Y_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _light_color_xy.y = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + y_received = true; + } + + if (attribute->id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_HUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + _light_color_hsv.h = attribute->data.value ? *(uint8_t *)attribute->data.value : 0; + h_received = true; + } + if (attribute->id == ESP_ZB_ZCL_ATTR_COLOR_CONTROL_CURRENT_SATURATION_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U8) { + _light_color_hsv.s = attribute->data.value ? *(uint8_t *)attribute->data.value : 0; + s_received = true; + } + + // Process XY color if both X and Y have been received + if (x_received && y_received) { + _light_color_rgb = espXYToRgbColor(255, _light_color_xy.x, _light_color_xy.y, false); + if (_on_light_color_change) { + _on_light_color_change(_light_color_rgb.r, _light_color_rgb.g, _light_color_rgb.b); + } + if (_on_light_color_change_with_source) { + _on_light_color_change_with_source(_light_color_rgb.r, _light_color_rgb.g, _light_color_rgb.b, src_endpoint, src_address); + } + x_received = false; // Reset flags after processing + y_received = false; + } + + // Process HS color if both H and S have been received + if (h_received && s_received) { + _light_color_rgb = espHsvColorToRgbColor(_light_color_hsv); + if (_on_light_color_change) { + _on_light_color_change(_light_color_rgb.r, _light_color_rgb.g, _light_color_rgb.b); + } + if (_on_light_color_change_with_source) { + _on_light_color_change_with_source(_light_color_rgb.r, _light_color_rgb.g, _light_color_rgb.b, src_endpoint, src_address); + } + h_received = false; // Reset flags after processing + s_received = false; + } + } +} #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h index ada1f75fbb4..10484a6867f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Switch endpoint inherited from common EP class */ #pragma once @@ -44,18 +58,68 @@ class ZigbeeColorDimmerSwitch : public ZigbeeEP { void setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, uint16_t short_addr); void setLightColor(uint8_t red, uint8_t green, uint8_t blue, uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + void getLightState(); + void getLightState(uint16_t group_addr); + void getLightState(uint8_t endpoint, uint16_t short_addr); + void getLightState(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void getLightLevel(); + void getLightLevel(uint16_t group_addr); + void getLightLevel(uint8_t endpoint, uint16_t short_addr); + void getLightLevel(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void getLightColor(); + void getLightColor(uint16_t group_addr); + void getLightColor(uint8_t endpoint, uint16_t short_addr); + void getLightColor(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void getLightColorHS(); + void getLightColorHS(uint16_t group_addr); + void getLightColorHS(uint8_t endpoint, uint16_t short_addr); + void getLightColorHS(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void onLightStateChange(void (*callback)(bool)) { + _on_light_state_change = callback; + } + void onLightStateChangeWithSource(void (*callback)(bool, uint8_t, esp_zb_zcl_addr_t)) { + _on_light_state_change_with_source = callback; + } + void onLightLevelChange(void (*callback)(uint8_t)) { + _on_light_level_change = callback; + } + void onLightLevelChangeWithSource(void (*callback)(uint8_t, uint8_t, esp_zb_zcl_addr_t)) { + _on_light_level_change_with_source = callback; + } + void onLightColorChange(void (*callback)(uint8_t, uint8_t, uint8_t)) { + _on_light_color_change = callback; + } + void onLightColorChangeWithSource(void (*callback)(uint8_t, uint8_t, uint8_t, uint8_t, esp_zb_zcl_addr_t)) { + _on_light_color_change_with_source = callback; + } + private: // save instance of the class in order to use it in static functions static ZigbeeColorDimmerSwitch *_instance; zb_device_params_t *_device; + espHsvColor_t _light_color_hsv; + espXyColor_t _light_color_xy; + espRgbColor_t _light_color_rgb; + + void (*_on_light_state_change)(bool); + void (*_on_light_state_change_with_source)(bool, uint8_t, esp_zb_zcl_addr_t); + void (*_on_light_level_change)(uint8_t); + void (*_on_light_level_change_with_source)(uint8_t, uint8_t, esp_zb_zcl_addr_t); + void (*_on_light_color_change)(uint8_t, uint8_t, uint8_t); + void (*_on_light_color_change_with_source)(uint8_t, uint8_t, uint8_t, uint8_t, esp_zb_zcl_addr_t); + void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req); void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx); void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx); static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx); static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx); - void calculateXY(uint8_t red, uint8_t green, uint8_t blue, uint16_t &x, uint16_t &y); + void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) override; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp index ced8e43d6ea..35e05afb290 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeContactSwitch.h" #if CONFIG_ZB_ENABLED @@ -17,6 +31,7 @@ ZigbeeContactSwitch::ZigbeeContactSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom contact switch configuration zigbee_contact_switch_cfg_t contact_switch_cfg = ZIGBEE_DEFAULT_CONTACT_SWITCH_CONFIG(); @@ -30,15 +45,16 @@ void ZigbeeContactSwitch::setIASClientEndpoint(uint8_t ep_number) { } bool ZigbeeContactSwitch::setClosed() { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; log_v("Setting Contact switch to closed"); uint8_t closed = 0; // ALARM1 = 0, ALARM2 = 0 esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_set_attribute_val( + ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &closed, false ); esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to set contact switch to closed: 0x%x: %s", ret, esp_err_to_name(ret)); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set contact switch to closed: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); return false; } _zone_status = closed; @@ -46,15 +62,16 @@ bool ZigbeeContactSwitch::setClosed() { } bool ZigbeeContactSwitch::setOpen() { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; log_v("Setting Contact switch to open"); uint8_t open = ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM1 | ESP_ZB_ZCL_IAS_ZONE_ZONE_STATUS_ALARM2; // ALARM1 = 1, ALARM2 = 1 esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_set_attribute_val( + ret = esp_zb_zcl_set_attribute_val( _endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONESTATUS_ID, &open, false ); esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to set contact switch to open: 0x%x: %s", ret, esp_err_to_name(ret)); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set contact switch to open: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); return false; } _zone_status = open; @@ -76,12 +93,8 @@ bool ZigbeeContactSwitch::report() { status_change_notif_cmd.delay = 0; esp_zb_lock_acquire(portMAX_DELAY); - esp_err_t ret = esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); - if (ret != ESP_OK) { - log_e("Failed to send IAS Zone status changed notification: 0x%x: %s", ret, esp_err_to_name(ret)); - return false; - } log_v("IAS Zone status changed notification sent"); return true; } @@ -101,11 +114,58 @@ void ZigbeeContactSwitch::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enro ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeContactSwitch::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_CONTACT_SWITCH; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeContactSwitch::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h index b33effd8dfc..002ed722d78 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ #pragma once @@ -56,12 +70,24 @@ class ZigbeeContactSwitch : public ZigbeeEP { // Report the contact switch value, done automatically after setting the position bool report(); + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } + private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; uint8_t _zone_status; uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp index 05a7e5ad6c1..1318cbae082 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeDimmableLight.h" #if CONFIG_ZB_ENABLED @@ -5,6 +19,7 @@ ZigbeeDimmableLight::ZigbeeDimmableLight(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_DIMMABLE_LIGHT_DEVICE_ID; + _on_light_change = nullptr; zigbee_dimmable_light_cfg_t light_cfg = ZIGBEE_DEFAULT_DIMMABLE_LIGHT_CONFIG(); _cluster_list = zigbee_dimmable_light_clusters_create(&light_cfg); diff --git a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h index 747fdbafaef..da1d1067fad 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp index c5b62ee2b75..2a3a5c80498 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeDoorWindowHandle.h" #if CONFIG_ZB_ENABLED @@ -17,6 +31,7 @@ ZigbeeDoorWindowHandle::ZigbeeDoorWindowHandle(uint8_t endpoint) : ZigbeeEP(endp _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom door window handle configuration zigbee_door_window_handle_cfg_t door_window_handle_cfg = ZIGBEE_DEFAULT_DOOR_WINDOW_HANDLE_CONFIG(); @@ -94,9 +109,8 @@ bool ZigbeeDoorWindowHandle::report() { status_change_notif_cmd.zone_id = _zone_id; status_change_notif_cmd.delay = 0; - //NOTE: Check result of esp_zb_zcl_ias_zone_status_change_notif_cmd_req() and return true if success, false if failure esp_zb_lock_acquire(portMAX_DELAY); - esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); log_v("IAS Zone status changed notification sent"); return true; @@ -117,11 +131,58 @@ void ZigbeeDoorWindowHandle::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_e ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeDoorWindowHandle::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_DOOR_WINDOW_HANDLE; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeDoorWindowHandle::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h index efffd34b12f..cfaf7e772a0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee door window handle (IAS Zone) endpoint inherited from common EP class */ #pragma once @@ -60,12 +74,24 @@ class ZigbeeDoorWindowHandle : public ZigbeeEP { // Report the door/window handle value, done automatically after setting the position bool report(); + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } + private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; uint8_t _zone_status; uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp index 8fa456c967a..6a862bade98 100644 --- a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeElectricalMeasurement.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h index 74d4a718fe2..9cace1c4b6c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h +++ b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp index f4b32ce1200..2c9a1f77eca 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp @@ -1,8 +1,23 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeFanControl.h" #if CONFIG_ZB_ENABLED ZigbeeFanControl::ZigbeeFanControl(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID; //There is no FAN_CONTROL_DEVICE_ID in the Zigbee spec + _on_fan_mode_change = nullptr; //Create basic analog sensor clusters without configuration _cluster_list = esp_zb_zcl_cluster_list_create(); diff --git a/libraries/Zigbee/src/ep/ZigbeeFanControl.h b/libraries/Zigbee/src/ep/ZigbeeFanControl.h index 25b5862c5c4..75d614e0ed4 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFanControl.h +++ b/libraries/Zigbee/src/ep/ZigbeeFanControl.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp index 8a60af5a8e1..ddaee3ea88f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeFlowSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h index fa16b4a5636..0402dd15377 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Flow sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeGateway.cpp b/libraries/Zigbee/src/ep/ZigbeeGateway.cpp index b0be81395ca..2640efc4e60 100644 --- a/libraries/Zigbee/src/ep/ZigbeeGateway.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeGateway.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeGateway.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeGateway.h b/libraries/Zigbee/src/ep/ZigbeeGateway.h index 3925630c0b8..e5ec3c05eb0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeGateway.h +++ b/libraries/Zigbee/src/ep/ZigbeeGateway.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Gateway endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp index f1661c3a026..a2a6d364f9d 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeIlluminanceSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h index 133dfc315db..fdc369b4a55 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Illuminance sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.cpp b/libraries/Zigbee/src/ep/ZigbeeLight.cpp index edfac04fcdf..579498c4aa3 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeLight.cpp @@ -1,8 +1,23 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeLight.h" #if CONFIG_ZB_ENABLED ZigbeeLight::ZigbeeLight(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID; + _on_light_change = nullptr; esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG(); _cluster_list = esp_zb_on_off_light_clusters_create(&light_cfg); // use esp_zb_zcl_cluster_list_create() instead of esp_zb_on_off_light_clusters_create() diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.h b/libraries/Zigbee/src/ep/ZigbeeLight.h index 773fbb14ec5..ce88a7a9aad 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp b/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp new file mode 100644 index 00000000000..5c289192023 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp @@ -0,0 +1,455 @@ +#include "ZigbeeMultistate.h" +#if CONFIG_ZB_ENABLED + +// Workaround for ESP-ZIGBEE-SDK 1.6.6 known issue +#ifdef __cplusplus +extern "C" { +#endif +extern void esp_zb_zcl_multi_input_init_server(void); +extern void esp_zb_zcl_multi_input_init_client(void); + +void esp_zb_zcl_multistate_input_init_server(void) { + esp_zb_zcl_multi_input_init_server(); +} +void esp_zb_zcl_multistate_input_init_client(void) { + esp_zb_zcl_multi_input_init_client(); +} +#ifdef __cplusplus +} +#endif + +ZigbeeMultistate::ZigbeeMultistate(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; + + //Create basic multistate clusters without configuration + _cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0}; + + // Initialize member variables + _multistate_clusters = 0; + _input_state = 0; + _output_state = 0; + _input_state_names_length = 0; + _output_state_names_length = 0; + // _input_state_names = nullptr; + // _output_state_names = nullptr; + _on_multistate_output_change = nullptr; +} + +bool ZigbeeMultistate::addMultistateInput() { + esp_zb_multistate_input_cluster_cfg_t multistate_input_cfg = { + .number_of_states = 3, .out_of_service = false, .present_value = 0, .status_flags = ESP_ZB_ZCL_MULTI_VALUE_STATUS_FLAGS_NORMAL + }; + + esp_zb_attribute_list_t *multistate_input_cluster = esp_zb_multistate_input_cluster_create(&multistate_input_cfg); + + // Create default description for Multistate Input + char default_description[] = "\x10" // Size of the description text + "Multistate Input"; // Description text + uint32_t application_type = 0x00000000 | (0x0D << 24); // Application type + // const char* state_text[] = { "Off", "On", "Auto" }; // State text array + + esp_err_t ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_STATE_TEXT_ID, (void *)state_text); + // if (ret != ESP_OK) { + // log_e("Failed to add state text attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + // return false; + // } + + ret = esp_zb_cluster_list_add_multistate_input_cluster(_cluster_list, multistate_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Multistate Input cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _multistate_clusters |= MULTISTATE_INPUT; + return true; +} + +bool ZigbeeMultistate::addMultistateOutput() { + esp_zb_multistate_output_cluster_cfg_t multistate_output_cfg = { + .number_of_states = 3, .out_of_service = false, .present_value = 0, .status_flags = ESP_ZB_ZCL_MULTI_VALUE_STATUS_FLAGS_NORMAL + }; + + esp_zb_attribute_list_t *multistate_output_cluster = esp_zb_multistate_output_cluster_create(&multistate_output_cfg); + + // Create default description for Multistate Output + char default_description[] = "\x11" // Size of the description text + "Multistate Output"; // Description text + uint32_t application_type = 0x00000000 | (0x0E << 24); + // const char* state_text[] = { "Off", "On", "Auto" }; // State text array + + esp_err_t ret = + esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // ret = esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_STATE_TEXT_ID, (void *)state_text); + // if (ret != ESP_OK) { + // log_e("Failed to add state text attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + // return false; + // } + + ret = esp_zb_cluster_list_add_multistate_output_cluster(_cluster_list, multistate_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Multistate Output cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _multistate_clusters |= MULTISTATE_OUTPUT; + return true; +} + +bool ZigbeeMultistate::setMultistateInputApplication(uint32_t application_type) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + // Add the Multistate Input group ID (0x0D) to the application type + uint32_t application_type_value = (0x0D << 24) | application_type; + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Multistate Input application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutputApplication(uint32_t application_type) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + // Add the Multistate Output group ID (0x0E) to the application type + uint32_t application_type_value = (0x0E << 24) | application_type; + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Multistate Output application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateInputDescription(const char *description) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the multistate input cluster + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutputDescription(const char *description) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the multistate output cluster + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateInputStates(uint16_t number_of_states) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID, (void *)&number_of_states); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _input_state_names_length = number_of_states; + return true; +} + +bool ZigbeeMultistate::setMultistateOutputStates(uint16_t number_of_states) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_NUMBER_OF_STATES_ID, (void *)&number_of_states); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _output_state_names_length = number_of_states; + return true; +} + +/* TODO: revisit this after arrays are supported + +bool ZigbeeMultistate::setMultistateInputStates(const char * const states[], uint16_t states_length) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_STATE_TEXT_ID, (void *)states); + if (ret != ESP_OK) { + log_e("Failed to set states text: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID, (void *)&states_length); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // Store state names locally for getter access + _input_state_names = states; + _input_state_names_length = states_length; + return true; +} + +bool ZigbeeMultistate::setMultistateOutputStates(const char * const states[], uint16_t states_length) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_STATE_TEXT_ID, (void *)states); + if (ret != ESP_OK) { + log_e("Failed to set states text: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_NUMBER_OF_STATES_ID, (void *)&states_length); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // Store state names locally for getter access + _output_state_names = states; + _output_state_names_length = states_length; + return true; +} +*/ + +//set attribute method -> method overridden in child class +void ZigbeeMultistate::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _output_state = *(uint16_t *)message->attribute.data.value; + multistateOutputChanged(); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for Multistate Output", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Multistate endpoint", message->info.cluster); + } +} + +void ZigbeeMultistate::multistateOutputChanged() { + if (_on_multistate_output_change) { + _on_multistate_output_change(_output_state); + } else { + log_w("No callback function set for multistate output change"); + } +} + +bool ZigbeeMultistate::setMultistateInput(uint16_t state) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + log_d("Setting multistate input to %d", state); + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID, &state, false + ); + esp_zb_lock_release(); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set multistate input: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutput(uint16_t state) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _output_state = state; + multistateOutputChanged(); + + log_v("Updating multistate output to %d", state); + /* Update multistate output */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID, &_output_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set multistate output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::reportMultistateInput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Multistate Input report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Multistate Input report sent"); + return true; +} + +bool ZigbeeMultistate::reportMultistateOutput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Multistate Output report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Multistate Output report sent"); + return true; +} + +#endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeMultistate.h b/libraries/Zigbee/src/ep/ZigbeeMultistate.h new file mode 100644 index 00000000000..e4eb0be267c --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeMultistate.h @@ -0,0 +1,188 @@ +/* Class of Zigbee Multistate sensor endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +// Types for Multistate Input/Output +// uint16_t for present value -> index to array of states +// uint16_t for number of states +// uint8_t chars for state names, with max of 16 chars ASCII + +// Multistate Input/Output Application Types Defines +#define ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX 0x0000 +#define ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES \ + (const char *const[]) { \ + "Off", "On", "Auto" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX 0x0001 +#define ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Low", "Medium", "High" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX 0x0002 +#define ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES 7 +#define ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES \ + (const char *const[]) { \ + "Auto", "Heat", "Cool", "Off", "Emergency Heat", "Fan Only", "Max Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX 0x0003 +#define ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES \ + (const char *const[]) { \ + "Occupied", "Unoccupied", "Standby", "Bypass" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX 0x0004 +#define ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES \ + (const char *const[]) { \ + "Inactive", "Active", "Hold" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX 0x0005 +#define ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES 8 +#define ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES \ + (const char *const[]) { \ + "Auto", "Warm-up", "Water Flush", "Autocalibration", "Shutdown Open", "Shutdown Closed", "Low Limit", "Test and Balance" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX 0x0006 +#define ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES 6 +#define ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Auto", "Heat Cool", "Heat Only", "Cool Only", "Fan Only" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX 0x0007 +#define ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES \ + (const char *const[]) { \ + "High", "Normal", "Low" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX 0x0008 +#define ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES \ + (const char *const[]) { \ + "Occupied", "Unoccupied", "Startup", "Shutdown" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX 0x0009 +#define ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES \ + (const char *const[]) { \ + "Night", "Day", "Hold" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX 0x000A +#define ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES 5 +#define ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Cool", "Heat", "Auto", "Emergency Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX 0x000B +#define ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES 7 +#define ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES \ + (const char *const[]) { \ + "Shutdown Closed", "Shutdown Open", "Satisfied", "Mixing", "Cooling", "Heating", "Suppl Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX 0xFFFF + +//enum for bits set to check what multistate cluster were added +enum zigbee_multistate_clusters { + MULTISTATE_INPUT = 1, + MULTISTATE_OUTPUT = 2 +}; + +typedef struct zigbee_multistate_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; +} zigbee_multistate_cfg_t; + +class ZigbeeMultistate : public ZigbeeEP { +public: + ZigbeeMultistate(uint8_t endpoint); + ~ZigbeeMultistate() {} + + // Add multistate clusters + bool addMultistateInput(); + bool addMultistateOutput(); + + // Set the application type and description for the multistate input + bool setMultistateInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_multistate_input.h for application type values + bool setMultistateInputDescription(const char *description); + bool setMultistateInputStates(uint16_t number_of_states); + // bool setMultistateInputStates(const char * const states[], uint16_t states_length); + + // Set the application type and description for the multistate output + bool setMultistateOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_multistate_output.h for application type values + bool setMultistateOutputDescription(const char *description); + bool setMultistateOutputStates(uint16_t number_of_states); + // bool setMultistateOutputStates(const char * const states[], uint16_t states_length); + + // Use to set a cb function to be called on multistate output change + void onMultistateOutputChange(void (*callback)(uint16_t state)) { + _on_multistate_output_change = callback; + } + + // Set the Multistate Input/Output value + bool setMultistateInput(uint16_t state); + bool setMultistateOutput(uint16_t state); + + // Get the Multistate Input/Output values + uint16_t getMultistateInput() { + return _input_state; + } + uint16_t getMultistateOutput() { + return _output_state; + } + + // Get state names and length + uint16_t getMultistateInputStateNamesLength() { + return _input_state_names_length; + } + uint16_t getMultistateOutputStateNamesLength() { + return _output_state_names_length; + } + // const char* const* getMultistateInputStateNames() { + // return _input_state_names; + // } + // const char* const* getMultistateOutputStateNames() { + // return _output_state_names; + // } + + // Report Multistate Input/Output + bool reportMultistateInput(); + bool reportMultistateOutput(); + +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + + void (*_on_multistate_output_change)(uint16_t state); + void multistateOutputChanged(); + + uint8_t _multistate_clusters; + uint16_t _output_state; + uint16_t _input_state; + + // Local storage for state names + uint16_t _input_state_names_length; + uint16_t _output_state_names_length; + // const char* const* _input_state_names; + // const char* const* _output_state_names; +}; + +#endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp index b8f88fed4a4..959fffdbbc3 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeOccupancySensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h index 7408e10a76b..9f1b190a18a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp index d25d15e5de3..fc2ca820c9a 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePM25Sensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h index 344f3e1f479..e4b47574704 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee PM2.5 sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp index b96ba672c04..250bd657315 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp @@ -1,8 +1,23 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePowerOutlet.h" #if CONFIG_ZB_ENABLED ZigbeePowerOutlet::ZigbeePowerOutlet(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_MAINS_POWER_OUTLET_DEVICE_ID; + _on_state_change = nullptr; esp_zb_mains_power_outlet_cfg_t outlet_cfg = ESP_ZB_DEFAULT_MAINS_POWER_OUTLET_CONFIG(); _cluster_list = esp_zb_mains_power_outlet_clusters_create(&outlet_cfg); diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h index 1ddb19587c7..231f7b6d3b9 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Power outlet endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp index bca06a35d0c..4fdf8600fbe 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePressureSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h index f93df7a7411..cd38a24d4a0 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once @@ -24,8 +38,8 @@ .pressure_meas_cfg = \ { \ .measured_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_VALUE_DEFAULT_VALUE, \ - .min_value = ESP_ZB_ZCL_PATTR_RESSURE_MEASUREMENT_MIN_VALUE_DEFAULT_VALUE, \ - .max_value = ESP_ZB_ZCL_PATTR_RESSURE_MEASUREMENT_MAX_VALUE_DEFAULT_VALUE, \ + .min_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MIN_VALUE_DEFAULT_VALUE, \ + .max_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MAX_VALUE_DEFAULT_VALUE, \ }, \ } // clang-format on diff --git a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp index 20db20d758a..b12cde268c0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeRangeExtender.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h index f9e4a963164..8d4c1566415 100644 --- a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h +++ b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Range Extender endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp index 68e7a7430cc..2660dd7b3d6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeSwitch.h" #if CONFIG_ZB_ENABLED @@ -8,6 +22,8 @@ ZigbeeSwitch::ZigbeeSwitch(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID; _instance = this; // Set the static pointer to this instance _device = nullptr; + _on_light_state_change = nullptr; + _on_light_state_change_with_source = nullptr; esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG(); _cluster_list = esp_zb_on_off_switch_clusters_create(&switch_cfg); @@ -52,7 +68,8 @@ void ZigbeeSwitch::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t ZigbeeSwitch *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_d("Found light endpoint"); - esp_zb_zdo_bind_req_param_t bind_req = {0}; + esp_zb_zdo_bind_req_param_t bind_req; + memset(&bind_req, 0, sizeof(bind_req)); zb_device_params_t *light = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); light->endpoint = endpoint; light->short_addr = addr; @@ -94,7 +111,8 @@ void ZigbeeSwitch::findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) { // Methods to control the light void ZigbeeSwitch::lightToggle() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_TOGGLE_ID; @@ -109,7 +127,8 @@ void ZigbeeSwitch::lightToggle() { void ZigbeeSwitch::lightToggle(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -125,7 +144,8 @@ void ZigbeeSwitch::lightToggle(uint16_t group_addr) { void ZigbeeSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -142,7 +162,8 @@ void ZigbeeSwitch::lightToggle(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -162,7 +183,8 @@ void ZigbeeSwitch::lightToggle(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOn() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_ON_ID; @@ -177,7 +199,8 @@ void ZigbeeSwitch::lightOn() { void ZigbeeSwitch::lightOn(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -193,7 +216,8 @@ void ZigbeeSwitch::lightOn(uint16_t group_addr) { void ZigbeeSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -210,7 +234,8 @@ void ZigbeeSwitch::lightOn(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -230,7 +255,8 @@ void ZigbeeSwitch::lightOn(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOff() { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_cmd_id = ESP_ZB_ZCL_CMD_ON_OFF_OFF_ID; @@ -245,7 +271,8 @@ void ZigbeeSwitch::lightOff() { void ZigbeeSwitch::lightOff(uint16_t group_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; @@ -261,7 +288,8 @@ void ZigbeeSwitch::lightOff(uint16_t group_addr) { void ZigbeeSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; @@ -278,7 +306,8 @@ void ZigbeeSwitch::lightOff(uint8_t endpoint, uint16_t short_addr) { void ZigbeeSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { if (_is_bound) { - esp_zb_zcl_on_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.zcl_basic_cmd.dst_endpoint = endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; @@ -298,7 +327,8 @@ void ZigbeeSwitch::lightOff(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { void ZigbeeSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) { if (_is_bound) { - esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_off_with_effect_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.effect_id = effect_id; @@ -314,7 +344,8 @@ void ZigbeeSwitch::lightOffWithEffect(uint8_t effect_id, uint8_t effect_variant) void ZigbeeSwitch::lightOnWithSceneRecall() { if (_is_bound) { - esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_on_with_recall_global_scene_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; log_v("Sending 'light on with scene recall' command"); @@ -325,9 +356,11 @@ void ZigbeeSwitch::lightOnWithSceneRecall() { log_e("Light not bound"); } } + void ZigbeeSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, uint16_t time_off) { if (_is_bound) { - esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req = {0}; + esp_zb_zcl_on_off_on_with_timed_off_cmd_t cmd_req; + memset(&cmd_req, 0, sizeof(cmd_req)); cmd_req.zcl_basic_cmd.src_endpoint = _endpoint; cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; cmd_req.on_off_control = on_off_control; //TODO: Test how it works, then maybe change API @@ -342,4 +375,86 @@ void ZigbeeSwitch::lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, } } +void ZigbeeSwitch::getLightState() { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeSwitch::getLightState(uint16_t group_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeSwitch::getLightState(uint8_t endpoint, uint16_t short_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeSwitch::getLightState(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + if (_is_bound) { + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_ON_OFF; + read_req.attr_number = 1; + uint16_t attr_id = ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID; + read_req.attr_field = &attr_id; + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + } +} + +void ZigbeeSwitch::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) { + if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) { + if (attribute->id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { + bool light_state = attribute->data.value ? *(bool *)attribute->data.value : false; + if (_on_light_state_change) { + _on_light_state_change(light_state); + } + if (_on_light_state_change_with_source) { + _on_light_state_change_with_source(light_state, src_endpoint, src_address); + } + } + } +} #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.h b/libraries/Zigbee/src/ep/ZigbeeSwitch.h index 0e7011c3312..09c17355763 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Switch endpoint inherited from common EP class */ #pragma once @@ -34,15 +48,32 @@ class ZigbeeSwitch : public ZigbeeEP { void lightOnWithTimedOff(uint8_t on_off_control, uint16_t time_on, uint16_t time_off); void lightOnWithSceneRecall(); + void getLightState(); + void getLightState(uint16_t group_addr); + void getLightState(uint8_t endpoint, uint16_t short_addr); + void getLightState(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void onLightStateChange(void (*callback)(bool)) { + _on_light_state_change = callback; + } + void onLightStateChangeWithSource(void (*callback)(bool, uint8_t, esp_zb_zcl_addr_t)) { + _on_light_state_change_with_source = callback; + } + private: // save instance of the class in order to use it in static functions static ZigbeeSwitch *_instance; zb_device_params_t *_device; + + void (*_on_light_state_change)(bool); + void (*_on_light_state_change_with_source)(bool, uint8_t, esp_zb_zcl_addr_t); + void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req); void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx); void findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx); static void findCbWrapper(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx); static void bindCbWrapper(esp_zb_zdp_status_t zdo_status, void *user_ctx); + void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) override; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp index 7126dae15cf..484506e139d 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeTempSensor.h" #if CONFIG_ZB_ENABLED @@ -113,10 +127,10 @@ bool ZigbeeTempSensor::reportTemperature() { } void ZigbeeTempSensor::addHumiditySensor(float min, float max, float tolerance) { - int16_t zb_min = zb_float_to_s16(min); - int16_t zb_max = zb_float_to_s16(max); + uint16_t zb_min = (uint16_t)(min * 100); + uint16_t zb_max = (uint16_t)(max * 100); uint16_t zb_tolerance = (uint16_t)(tolerance * 100); - int16_t default_hum = ESP_ZB_ZCL_REL_HUMIDITY_MEASUREMENT_MEASURED_VALUE_DEFAULT; + uint16_t default_hum = ESP_ZB_ZCL_REL_HUMIDITY_MEASUREMENT_MEASURED_VALUE_DEFAULT; esp_zb_attribute_list_t *humidity_cluster = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT); esp_zb_humidity_meas_cluster_add_attr(humidity_cluster, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, &default_hum); esp_zb_humidity_meas_cluster_add_attr(humidity_cluster, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID, &zb_min); @@ -128,7 +142,7 @@ void ZigbeeTempSensor::addHumiditySensor(float min, float max, float tolerance) bool ZigbeeTempSensor::setHumidity(float humidity) { esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; - int16_t zb_humidity = zb_float_to_s16(humidity); + uint16_t zb_humidity = (uint16_t)(humidity * 100); log_v("Updating humidity sensor value..."); /* Update humidity sensor measured value */ log_d("Setting humidity to %d", zb_humidity); diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h index bc769b32de6..e7219c17335 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Temperature + Humidity sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp index f3bfd7d981b..bf69ce85717 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeThermostat.h" #if CONFIG_ZB_ENABLED @@ -12,6 +26,12 @@ ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID; _instance = this; // Set the static pointer to this instance _device = nullptr; // Initialize sensor pointer to null + _on_temp_receive = nullptr; + _on_temp_receive_with_source = nullptr; + _on_temp_config_receive = nullptr; + _on_humidity_receive = nullptr; + _on_humidity_receive_with_source = nullptr; + _on_humidity_config_receive = nullptr; //use custom config to avoid narrowing error -> must be fixed in zigbee-sdk esp_zb_thermostat_cfg_t thermostat_cfg = ZB_DEFAULT_THERMOSTAT_CONFIG(); @@ -25,6 +45,7 @@ ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) { esp_zb_cluster_list_add_thermostat_cluster(_cluster_list, esp_zb_thermostat_cluster_create(&(thermostat_cfg.thermostat_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); /* Add temperature measurement cluster for attribute reporting */ esp_zb_cluster_list_add_temperature_meas_cluster(_cluster_list, esp_zb_temperature_meas_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE); + esp_zb_cluster_list_add_humidity_meas_cluster(_cluster_list, esp_zb_humidity_meas_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE); _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID, .app_device_version = 0}; } @@ -65,7 +86,8 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin ZigbeeThermostat *instance = static_cast(user_ctx); if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) { log_i("Found temperature sensor"); - esp_zb_zdo_bind_req_param_t bind_req = {0}; + esp_zb_zdo_bind_req_param_t bind_req; + memset(&bind_req, 0, sizeof(bind_req)); /* Store the information of the remote device */ zb_device_params_t *sensor = (zb_device_params_t *)malloc(sizeof(zb_device_params_t)); sensor->endpoint = endpoint; @@ -85,21 +107,27 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin log_i("Request temperature sensor to bind us"); esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, NULL); + log_i("Request humidity sensor to bind us"); + bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, NULL); + /* 2. Send binding request to self */ bind_req.req_dst_addr = esp_zb_get_short_address(); /* populate the src information of the binding */ esp_zb_get_long_address(bind_req.src_address); bind_req.src_endp = instance->_endpoint; - bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED; memcpy(bind_req.dst_address_u.addr_long, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t)); bind_req.dst_endp = endpoint; - log_i("Try to bind Temperature Measurement"); - //save sensor params in the class + log_i("Try to bind Humidity Measurement"); // Optional cluster to bind, if fails, continue to bind the temperature measurement cluster + // save sensor params in the class instance->_device = sensor; - log_d("Find callback on EP %d", instance->_endpoint); + esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, NULL); + bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; + log_i("Try to bind Temperature Measurement"); // Mandatory cluster to bind esp_zb_zdo_device_bind_req(&bind_req, ZigbeeThermostat::bindCbWrapper, this); } else { log_d("No temperature sensor endpoint found"); @@ -107,9 +135,9 @@ void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uin } void ZigbeeThermostat::findEndpoint(esp_zb_zdo_match_desc_req_param_t *param) { - uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT}; + uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT}; param->profile_id = ESP_ZB_AF_HA_PROFILE_ID; - param->num_in_clusters = 1; + param->num_in_clusters = 2; param->num_out_clusters = 0; param->cluster_list = cluster_list; esp_zb_zdo_match_cluster(param, ZigbeeThermostat::findCbWrapper, this); @@ -141,21 +169,60 @@ void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_att } if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { uint16_t tolerance = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; - _tolerance = 1.0 * tolerance / 100; + _tolerance_temp = 1.0 * tolerance / 100; read_config++; - log_d("Received tolerance: %.2f°C from endpoint %d", _tolerance, src_endpoint); + log_d("Received tolerance: %.2f°C from endpoint %d", _tolerance_temp, src_endpoint); } if (read_config == 3) { - log_d("All config attributes processed"); + log_d("All temperature config attributes processed"); read_config = 0; xSemaphoreGive(lock); } } + static uint8_t read_humidity_config = 0; + if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT) { + if (attribute->id == ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + uint16_t value = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + float humidity = 1.0 * value / 100; + if (_on_humidity_receive) { + _on_humidity_receive(humidity); + } + if (_on_humidity_receive_with_source) { + _on_humidity_receive_with_source(humidity, src_endpoint, src_address); + } + } + if (attribute->id == ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + uint16_t min_value = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + _min_humidity = 1.0 * min_value / 100; + read_humidity_config++; + log_d("Received min humidity: %.2f%% from endpoint %d", _min_humidity, src_endpoint); + } + if (attribute->id == ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MAX_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + uint16_t max_value = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + _max_humidity = 1.0 * max_value / 100; + read_humidity_config++; + log_d("Received max humidity: %.2f%% from endpoint %d", _max_humidity, src_endpoint); + } + if (attribute->id == ESP_ZB_ZCL_ATTR_REL_HUMIDITY_TOLERANCE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + uint16_t tolerance = attribute->data.value ? *(uint16_t *)attribute->data.value : 0; + _tolerance_humidity = 1.0 * tolerance / 100; + read_humidity_config++; + log_d("Received tolerance: %.2f%% from endpoint %d", _tolerance_humidity, src_endpoint); + } + if (read_humidity_config == 3) { + log_d("All humidity config attributes processed"); + read_humidity_config = 0; + xSemaphoreGive(lock); + } + } } +// Temperature measuring methods + void ZigbeeThermostat::getTemperature() { /* Send "read attributes" command to all bound sensors */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; @@ -172,7 +239,8 @@ void ZigbeeThermostat::getTemperature() { void ZigbeeThermostat::getTemperature(uint16_t group_addr) { /* Send "read attributes" command to the group */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; @@ -190,7 +258,8 @@ void ZigbeeThermostat::getTemperature(uint16_t group_addr) { void ZigbeeThermostat::getTemperature(uint8_t endpoint, uint16_t short_addr) { /* Send "read attributes" command to specific endpoint */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_endpoint = endpoint; @@ -209,7 +278,8 @@ void ZigbeeThermostat::getTemperature(uint8_t endpoint, uint16_t short_addr) { void ZigbeeThermostat::getTemperature(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { /* Send "read attributes" command to specific endpoint */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_endpoint = endpoint; @@ -229,9 +299,10 @@ void ZigbeeThermostat::getTemperature(uint8_t endpoint, esp_zb_ieee_addr_t ieee_ esp_zb_lock_release(); } -void ZigbeeThermostat::getSensorSettings() { +void ZigbeeThermostat::getTemperatureSettings() { /* Send "read attributes" command to all bound sensors */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; @@ -253,13 +324,14 @@ void ZigbeeThermostat::getSensorSettings() { return; } else { //Call the callback function when all attributes are read - _on_config_receive(_min_temp, _max_temp, _tolerance); + _on_temp_config_receive(_min_temp, _max_temp, _tolerance_temp); } } -void ZigbeeThermostat::getSensorSettings(uint16_t group_addr) { +void ZigbeeThermostat::getTemperatureSettings(uint16_t group_addr) { /* Send "read attributes" command to the group */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; @@ -282,13 +354,14 @@ void ZigbeeThermostat::getSensorSettings(uint16_t group_addr) { return; } else { //Call the callback function when all attributes are read - _on_config_receive(_min_temp, _max_temp, _tolerance); + _on_temp_config_receive(_min_temp, _max_temp, _tolerance_temp); } } -void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, uint16_t short_addr) { +void ZigbeeThermostat::getTemperatureSettings(uint8_t endpoint, uint16_t short_addr) { /* Send "read attributes" command to specific endpoint */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_endpoint = endpoint; @@ -312,13 +385,14 @@ void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, uint16_t short_addr) return; } else { //Call the callback function when all attributes are read - _on_config_receive(_min_temp, _max_temp, _tolerance); + _on_temp_config_receive(_min_temp, _max_temp, _tolerance_temp); } } -void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { +void ZigbeeThermostat::getTemperatureSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { /* Send "read attributes" command to specific endpoint */ - esp_zb_zcl_read_attr_cmd_t read_req = {0}; + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; read_req.zcl_basic_cmd.src_endpoint = _endpoint; read_req.zcl_basic_cmd.dst_endpoint = endpoint; @@ -345,13 +419,14 @@ void ZigbeeThermostat::getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ie return; } else { //Call the callback function when all attributes are read - _on_config_receive(_min_temp, _max_temp, _tolerance); + _on_temp_config_receive(_min_temp, _max_temp, _tolerance_temp); } } void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t max_interval, float delta) { /* Send "configure report attribute" command to all bound sensors */ - esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT; @@ -370,7 +445,7 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t m report_cmd.record_number = ZB_ARRAY_LENGHT(records); report_cmd.record_field = records; - log_i("Sending 'configure reporting' command"); + log_i("Sending 'configure temperature reporting' command"); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_config_report_cmd_req(&report_cmd); esp_zb_lock_release(); @@ -378,7 +453,8 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t m void ZigbeeThermostat::setTemperatureReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta) { /* Send "configure report attribute" command to the group */ - esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; report_cmd.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; @@ -398,7 +474,7 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t group_addr, uint16_t min report_cmd.record_number = ZB_ARRAY_LENGHT(records); report_cmd.record_field = records; - log_i("Sending 'configure reporting' command to group address 0x%x", group_addr); + log_i("Sending 'configure temperature reporting' command to group address 0x%x", group_addr); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_config_report_cmd_req(&report_cmd); esp_zb_lock_release(); @@ -406,7 +482,8 @@ void ZigbeeThermostat::setTemperatureReporting(uint16_t group_addr, uint16_t min void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta) { /* Send "configure report attribute" command to specific endpoint */ - esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; @@ -427,7 +504,7 @@ void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, uint16_t short_ report_cmd.record_number = ZB_ARRAY_LENGHT(records); report_cmd.record_field = records; - log_i("Sending 'configure reporting' command to endpoint %d, address 0x%x", endpoint, short_addr); + log_i("Sending 'configure temperature reporting' command to endpoint %d, address 0x%x", endpoint, short_addr); esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_config_report_cmd_req(&report_cmd); esp_zb_lock_release(); @@ -435,7 +512,8 @@ void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, uint16_t short_ void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta) { /* Send "configure report attribute" command to specific endpoint */ - esp_zb_zcl_config_report_cmd_t report_cmd = {0}; + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; @@ -457,12 +535,337 @@ void ZigbeeThermostat::setTemperatureReporting(uint8_t endpoint, esp_zb_ieee_add report_cmd.record_field = records; log_i( - "Sending 'configure reporting' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], + "Sending 'configure temperature reporting' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], + ieee_addr[6], ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + +// Humidity measuring methods + +void ZigbeeThermostat::getHumidity() { + /* Send "read attributes" command to all bound sensors */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity' command"); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getHumidity(uint16_t group_addr) { + /* Send "read attributes" command to the group */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getHumidity(uint8_t endpoint, uint16_t short_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getHumidity(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID}; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i( + "Sending 'read humidity' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], ieee_addr[5], + ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::getHumiditySettings() { + /* Send "read attributes" command to all bound sensors */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity settings' command"); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_humidity_config_receive(_min_humidity, _max_humidity, _tolerance_humidity); + } +} + +void ZigbeeThermostat::getHumiditySettings(uint16_t group_addr) { + /* Send "read attributes" command to the group */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity settings' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_humidity_config_receive(_min_humidity, _max_humidity, _tolerance_humidity); + } +} + +void ZigbeeThermostat::getHumiditySettings(uint8_t endpoint, uint16_t short_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i("Sending 'read humidity settings' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_humidity_config_receive(_min_humidity, _max_humidity, _tolerance_humidity); + } +} + +void ZigbeeThermostat::getHumiditySettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + /* Send "read attributes" command to specific endpoint */ + esp_zb_zcl_read_attr_cmd_t read_req; + memset(&read_req, 0, sizeof(read_req)); + read_req.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + read_req.zcl_basic_cmd.src_endpoint = _endpoint; + read_req.zcl_basic_cmd.dst_endpoint = endpoint; + read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + memcpy(read_req.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + uint16_t attributes[] = { + ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_REL_HUMIDITY_TOLERANCE_ID + }; + read_req.attr_number = ZB_ARRAY_LENGHT(attributes); + read_req.attr_field = attributes; + + log_i( + "Sending 'read humidity settings' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] ); esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_read_attr_cmd_req(&read_req); + esp_zb_lock_release(); + + //Take semaphore to wait for response of all attributes + if (xSemaphoreTake(lock, ZB_CMD_TIMEOUT) != pdTRUE) { + log_e("Error while reading attributes"); + return; + } else { + //Call the callback function when all attributes are read + _on_humidity_config_receive(_min_humidity, _max_humidity, _tolerance_humidity); + } +} + +void ZigbeeThermostat::setHumidityReporting(uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to all bound sensors */ + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_U16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i("Sending 'configure humidity reporting' command"); + esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zcl_config_report_cmd_req(&report_cmd); esp_zb_lock_release(); } +void ZigbeeThermostat::setHumidityReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to the group */ + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_GROUP_ENDP_NOT_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_addr_u.addr_short = group_addr; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_U16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i("Sending 'configure humidity reporting' command to group address 0x%x", group_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::setHumidityReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to specific endpoint */ + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; + report_cmd.zcl_basic_cmd.dst_addr_u.addr_short = short_addr; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_U16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i("Sending 'configure humidity reporting' command to endpoint %d, address 0x%x", endpoint, short_addr); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} + +void ZigbeeThermostat::setHumidityReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta) { + /* Send "configure report attribute" command to specific endpoint */ + esp_zb_zcl_config_report_cmd_t report_cmd; + memset(&report_cmd, 0, sizeof(report_cmd)); + report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_64_ENDP_PRESENT; + report_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_cmd.zcl_basic_cmd.dst_endpoint = endpoint; + report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY_MEASUREMENT; + memcpy(report_cmd.zcl_basic_cmd.dst_addr_u.addr_long, ieee_addr, sizeof(esp_zb_ieee_addr_t)); + + int16_t report_change = (int16_t)delta * 100; + esp_zb_zcl_config_report_record_t records[] = { + { + .direction = ESP_ZB_ZCL_REPORT_DIRECTION_SEND, + .attributeID = ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID, + .attrType = ESP_ZB_ZCL_ATTR_TYPE_U16, + .min_interval = min_interval, + .max_interval = max_interval, + .reportable_change = (void *)&report_change, + }, + }; + report_cmd.record_number = ZB_ARRAY_LENGHT(records); + report_cmd.record_field = records; + + log_i( + "Sending 'configure humidity reporting' command to endpoint %d, ieee address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", endpoint, ieee_addr[7], ieee_addr[6], + ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0] + ); + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_config_report_cmd_req(&report_cmd); + esp_zb_lock_release(); +} #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.h b/libraries/Zigbee/src/ep/ZigbeeThermostat.h index c10a9d7f974..57a4c934b79 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.h +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Temperature sensor endpoint inherited from common EP class */ #pragma once @@ -34,14 +48,20 @@ class ZigbeeThermostat : public ZigbeeEP { ZigbeeThermostat(uint8_t endpoint); ~ZigbeeThermostat() {} + // Temperature measuring methods void onTempReceive(void (*callback)(float)) { _on_temp_receive = callback; } void onTempReceiveWithSource(void (*callback)(float, uint8_t, esp_zb_zcl_addr_t)) { _on_temp_receive_with_source = callback; } + // For backward compatibility: keep onConfigReceive as an alias to onTempConfigReceive (deprecated). + [[deprecated("Use onTempConfigReceive instead.")]] void onConfigReceive(void (*callback)(float, float, float)) { - _on_config_receive = callback; + onTempConfigReceive(callback); + } + void onTempConfigReceive(void (*callback)(float, float, float)) { + _on_temp_config_receive = callback; } void getTemperature(); @@ -49,16 +69,60 @@ class ZigbeeThermostat : public ZigbeeEP { void getTemperature(uint8_t endpoint, uint16_t short_addr); void getTemperature(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); - void getSensorSettings(); - void getSensorSettings(uint16_t group_addr); - void getSensorSettings(uint8_t endpoint, uint16_t short_addr); - void getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + // For backward compatibility: keep getSensorSettings as an alias to getTemperatureSettings (deprecated). + [[deprecated("Use getTemperatureSettings instead.")]] + void getSensorSettings() { + getTemperatureSettings(); + } + [[deprecated("Use getTemperatureSettings instead.")]] + void getSensorSettings(uint16_t group_addr) { + getTemperatureSettings(group_addr); + } + [[deprecated("Use getTemperatureSettings instead.")]] + void getSensorSettings(uint8_t endpoint, uint16_t short_addr) { + getTemperatureSettings(endpoint, short_addr); + } + [[deprecated("Use getTemperatureSettings instead.")]] + void getSensorSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr) { + getTemperatureSettings(endpoint, ieee_addr); + } + + void getTemperatureSettings(); + void getTemperatureSettings(uint16_t group_addr); + void getTemperatureSettings(uint8_t endpoint, uint16_t short_addr); + void getTemperatureSettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); void setTemperatureReporting(uint16_t min_interval, uint16_t max_interval, float delta); void setTemperatureReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta); void setTemperatureReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta); void setTemperatureReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta); + // Humidity measuring methods + void onHumidityReceive(void (*callback)(float)) { + _on_humidity_receive = callback; + } + void onHumidityReceiveWithSource(void (*callback)(float, uint8_t, esp_zb_zcl_addr_t)) { + _on_humidity_receive_with_source = callback; + } + void onHumidityConfigReceive(void (*callback)(float, float, float)) { + _on_humidity_config_receive = callback; + } + + void getHumidity(); + void getHumidity(uint16_t group_addr); + void getHumidity(uint8_t endpoint, uint16_t short_addr); + void getHumidity(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void getHumiditySettings(); + void getHumiditySettings(uint16_t group_addr); + void getHumiditySettings(uint8_t endpoint, uint16_t short_addr); + void getHumiditySettings(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr); + + void setHumidityReporting(uint16_t min_interval, uint16_t max_interval, float delta); + void setHumidityReporting(uint16_t group_addr, uint16_t min_interval, uint16_t max_interval, float delta); + void setHumidityReporting(uint8_t endpoint, uint16_t short_addr, uint16_t min_interval, uint16_t max_interval, float delta); + void setHumidityReporting(uint8_t endpoint, esp_zb_ieee_addr_t ieee_addr, uint16_t min_interval, uint16_t max_interval, float delta); + private: // save instance of the class in order to use it in static functions static ZigbeeThermostat *_instance; @@ -66,10 +130,17 @@ class ZigbeeThermostat : public ZigbeeEP { void (*_on_temp_receive)(float); void (*_on_temp_receive_with_source)(float, uint8_t, esp_zb_zcl_addr_t); - void (*_on_config_receive)(float, float, float); + void (*_on_temp_config_receive)(float, float, float); float _min_temp; float _max_temp; - float _tolerance; + float _tolerance_temp; + + void (*_on_humidity_receive)(float); + void (*_on_humidity_receive_with_source)(float, uint8_t, esp_zb_zcl_addr_t); + void (*_on_humidity_config_receive)(float, float, float); + float _min_humidity; + float _max_humidity; + float _tolerance_humidity; void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req); void bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx); diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp index 6be457c389a..a9fd437a2c6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeVibrationSensor.h" #if CONFIG_ZB_ENABLED @@ -17,6 +31,7 @@ ZigbeeVibrationSensor::ZigbeeVibrationSensor(uint8_t endpoint) : ZigbeeEP(endpoi _zone_status = 0; _zone_id = 0xff; _ias_cie_endpoint = 1; + _enrolled = false; //Create custom vibration sensor configuration zigbee_vibration_sensor_cfg_t vibration_sensor_cfg = ZIGBEE_DEFAULT_VIBRATION_SENSOR_CONFIG(); @@ -43,11 +58,10 @@ bool ZigbeeVibrationSensor::setVibration(bool sensed) { return false; } _zone_status = vibration; - report(); - return true; + return report(); } -void ZigbeeVibrationSensor::report() { +bool ZigbeeVibrationSensor::report() { /* Send IAS Zone status changed notification command */ esp_zb_zcl_ias_zone_status_change_notif_cmd_t status_change_notif_cmd; @@ -61,9 +75,10 @@ void ZigbeeVibrationSensor::report() { status_change_notif_cmd.delay = 0; esp_zb_lock_acquire(portMAX_DELAY); - esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); + esp_zb_zcl_ias_zone_status_change_notif_cmd_req(&status_change_notif_cmd); //return transaction sequence number, ignore it esp_zb_lock_release(); log_v("IAS Zone status changed notification sent"); + return true; } void ZigbeeVibrationSensor::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) { @@ -81,11 +96,58 @@ void ZigbeeVibrationSensor::zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_en ); esp_zb_lock_release(); _zone_id = message->zone_id; + _enrolled = true; } - } else { log_w("Received message ignored. Cluster ID: %d not supported for On/Off Light", message->info.cluster); } } +bool ZigbeeVibrationSensor::requestIASZoneEnroll() { + esp_zb_zcl_ias_zone_enroll_request_cmd_t enroll_request; + enroll_request.zcl_basic_cmd.src_endpoint = _endpoint; + enroll_request.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + enroll_request.zone_type = ESP_ZB_ZCL_IAS_ZONE_ZONETYPE_VIBRATION_MOVEMENT; + enroll_request.manuf_code = 0; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_ias_zone_enroll_cmd_req(&enroll_request); //return transaction sequence number, ignore it + esp_zb_lock_release(); + log_v("IAS Zone enroll request sent"); + return true; +} + +bool ZigbeeVibrationSensor::restoreIASZoneEnroll() { + esp_zb_lock_acquire(portMAX_DELAY); + esp_zb_zcl_attr_t *ias_cie_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_IAS_CIE_ADDRESS_ID); + esp_zb_zcl_attr_t *zone_id_attr = + esp_zb_zcl_get_attribute(_endpoint, ESP_ZB_ZCL_CLUSTER_ID_IAS_ZONE, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_IAS_ZONE_ZONEID_ID); + esp_zb_lock_release(); + + if (ias_cie_attr == NULL || ias_cie_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: ias cie address attribute not found"); + return false; + } + if (zone_id_attr == NULL || zone_id_attr->data_p == NULL) { + log_e("Failed to restore IAS Zone enroll: zone id attribute not found"); + return false; + } + + memcpy(_ias_cie_addr, (esp_zb_ieee_addr_t *)ias_cie_attr->data_p, sizeof(esp_zb_ieee_addr_t)); + _zone_id = (*(uint8_t *)zone_id_attr->data_p); + + log_d( + "Restored IAS Zone enroll: zone id(%d), ias cie address(%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X)", _zone_id, _ias_cie_addr[0], _ias_cie_addr[1], + _ias_cie_addr[2], _ias_cie_addr[3], _ias_cie_addr[4], _ias_cie_addr[5], _ias_cie_addr[6], _ias_cie_addr[7] + ); + + if (_zone_id == 0xFF) { + log_e("Failed to restore IAS Zone enroll: zone id not valid"); + return false; + } + _enrolled = true; + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h index 2f67c7bb6b4..a819be0ba8f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ #pragma once @@ -51,7 +65,18 @@ class ZigbeeVibrationSensor : public ZigbeeEP { bool setVibration(bool sensed); // Report the vibration sensor value, done automatically after setting the sensed value - void report(); + bool report(); + + // Request a new IAS zone enroll, can be called to enroll a new device or to re-enroll an already enrolled device + bool requestIASZoneEnroll(); + + // Restore IAS Zone enroll, needed to be called after rebooting already enrolled device - restored from flash memory (faster for sleepy devices) + bool restoreIASZoneEnroll(); + + // Check if the device is enrolled in the IAS Zone + bool enrolled() { + return _enrolled; + } private: void zbIASZoneEnrollResponse(const esp_zb_zcl_ias_zone_enroll_response_message_t *message) override; @@ -59,6 +84,7 @@ class ZigbeeVibrationSensor : public ZigbeeEP { uint8_t _zone_id; esp_zb_ieee_addr_t _ias_cie_addr; uint8_t _ias_cie_endpoint; + bool _enrolled; }; #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp index 72184927d4d..672b6254017 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeWindSpeedSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h index 641c1d84780..b5371f8fdef 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee WindSpeed sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index 7c7889dbbf7..f4e365753e5 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeWindowCovering.h" #if CONFIG_ZB_ENABLED @@ -48,6 +62,11 @@ esp_zb_cluster_list_t *ZigbeeWindowCovering::zigbee_window_covering_clusters_cre ZigbeeWindowCovering::ZigbeeWindowCovering(uint8_t endpoint) : ZigbeeEP(endpoint) { _device_id = ESP_ZB_HA_WINDOW_COVERING_DEVICE_ID; + _on_open = nullptr; + _on_close = nullptr; + _on_go_to_lift_percentage = nullptr; + _on_go_to_tilt_percentage = nullptr; + _on_stop = nullptr; // set default values for window covering attributes _current_lift_percentage = ESP_ZB_ZCL_WINDOW_COVERING_CURRENT_POSITION_LIFT_PERCENTAGE_DEFAULT_VALUE; diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h index 288d92c5765..f63e5f9e642 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Window Covering endpoint inherited from common EP class */ #pragma once diff --git a/package.json b/package.json index 4c3e4725a9d..b8e8085d815 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framework-arduinoespressif32", - "version": "3.3.0", + "version": "3.3.4", "description": "Arduino Wiring-based Framework for the Espressif ESP32, ESP32-P4, ESP32-S and ESP32-C series of SoCs", "keywords": [ "framework", diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 1b4b005dd34..96af1b6d286 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -6,7 +6,7 @@ "websiteURL": "https://github.com/espressif/arduino-esp32", "email": "hristo@espressif.com", "help": { - "online": "http://esp32.com" + "online": "https://esp32.com" }, "platforms": [ { @@ -51,37 +51,37 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-b66b5448-v1" + "version": "idf-release_v5.5-d66ebb86-v1" }, { "packager": "esp32", "name": "xtensa-esp-elf-gcc", - "version": "esp-14.2.0_20241119" + "version": "esp-14.2.0_20251107" }, { "packager": "esp32", "name": "xtensa-esp-elf-gdb", - "version": "16.2_20250324" + "version": "16.3_20250913" }, { "packager": "esp32", "name": "riscv32-esp-elf-gcc", - "version": "esp-14.2.0_20241119" + "version": "esp-14.2.0_20251107" }, { "packager": "esp32", "name": "riscv32-esp-elf-gdb", - "version": "16.2_20250324" + "version": "16.3_20250913" }, { "packager": "esp32", "name": "openocd-esp32", - "version": "v0.12.0-esp32-20250422" + "version": "v0.12.0-esp32-20250707" }, { "packager": "esp32", "name": "esptool_py", - "version": "5.0.2" + "version": "5.1.0" }, { "packager": "esp32", @@ -91,7 +91,7 @@ { "packager": "esp32", "name": "mklittlefs", - "version": "3.0.0-gnu12-dc7f933" + "version": "4.0.2-db0513a" }, { "packager": "arduino", @@ -104,476 +104,476 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-b66b5448-v1", + "version": "idf-release_v5.5-d66ebb86-v1", "systems": [ { "host": "i686-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-d66ebb86-v1.zip", + "checksum": "SHA-256:e44eefcf645cbf3d8f9270c0cdcc836621cbb10a8f5fac62e337d0da2e564fc8", + "size": "508996777" } ] }, { "name": "xtensa-esp-elf-gcc", - "version": "esp-14.2.0_20241119", + "version": "esp-14.2.0_20251107", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-x86_64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:b1859df334a85541ae746e1b86439f59180d87f8cf1cc04c2e770fadf9f006e9", - "size": "323678089" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-x86_64-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-x86_64-linux-gnu.tar.gz", + "checksum": "SHA-256:c8aced923fe9bb8d3614212aee94b9f354f1c47992ac987f74138997212e0393", + "size": "326311197" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-aarch64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:7ff023033a5c00e55b9fc0a0b26d18fb0e476c24e24c5b0459bcb2e05a3729f1", - "size": "320064691" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-aarch64-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-aarch64-linux-gnu.tar.gz", + "checksum": "SHA-256:d290e34fcf10ac53157add3de4954d8d10b732f7b7763b7494686ada1d7a4e49", + "size": "322742882" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-arm-linux-gnueabi.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:bb11dbf3ed25d4e0cc9e938749519e8236cfa2609e85742d311f1d869111805a", - "size": "319454139" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-arm-linux-gnueabi.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-arm-linux-gnueabi.tar.gz", + "checksum": "SHA-256:b026f393edf77401caeb35582574de02937d318c61ea6883ec5ffe266af96b6e", + "size": "322125748" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-i586-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:5ac611dca62ec791d413d1f417d566c444b006d2a4f97bd749b15f782d87249b", - "size": "328335914" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-i586-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-i586-linux-gnu.tar.gz", + "checksum": "SHA-256:4a4e6ca8f61cea063ccca9e79d8b5d0bcc6e867d0f5ad0bbab1b61a85ad5930b", + "size": "330996425" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-x86_64-apple-darwin_signed.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-x86_64-apple-darwin_signed.tar.gz", - "checksum": "SHA-256:15b3e60362028eaeff9156dc82dac3f1436b4aeef3920b28d7650974d8c34751", - "size": "336215844" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-x86_64-apple-darwin.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-x86_64-apple-darwin.tar.gz", + "checksum": "SHA-256:26a05cecf704c660ed5eeb90737be27135fd686267c63fda0fb8b4046880b7b5", + "size": "338872460" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-aarch64-apple-darwin_signed.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-aarch64-apple-darwin_signed.tar.gz", - "checksum": "SHA-256:45c475518735133789bacccad31f872318b7ecc0b31cc9b7924aad880034f0bf", - "size": "318797396" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-aarch64-apple-darwin.tar.gz", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-aarch64-apple-darwin.tar.gz", + "checksum": "SHA-256:4f2765f2ecbeaf5de42a26277462ca0dc3f013d15fe85f1f1b2d3a69633abb70", + "size": "321459468" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-i686-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-i686-w64-mingw32.zip", - "checksum": "SHA-256:b30e450e0af279783c54a9ae77c3b367dd556b78eda930a92ec7b784a74c28c8", - "size": "382457717" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-i686-w64-mingw32.zip", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-i686-w64-mingw32.zip", + "checksum": "SHA-256:70778f0e7dad518f9e6547911bedd6e23c48775313c7ed92f1cb04a86cea2351", + "size": "391481671" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/xtensa-esp-elf-14.2.0_20241119-x86_64-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-14.2.0_20241119-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:62ae704777d73c30689efff6e81178632a1ca44d1a2d60f4621eb997e040e028", - "size": "386316009" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/xtensa-esp-elf-14.2.0_20251107-x86_64-w64-mingw32.zip", + "archiveFileName": "xtensa-esp-elf-14.2.0_20251107-x86_64-w64-mingw32.zip", + "checksum": "SHA-256:b0de5062da2f05d1773d1537421134e2a7517bd74a06c3b5763b07f94e38bece", + "size": "396056136" } ] }, { "name": "xtensa-esp-elf-gdb", - "version": "16.2_20250324", + "version": "16.3_20250913", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-x86_64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:27b58ab12248e04277c4fdc74038cf0a001d5142df091ab94939ad35053738fd", - "size": "36361058" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-x86_64-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-x86_64-linux-gnu.tar.gz", + "checksum": "SHA-256:16d05c9104ff84529ac3799abb04d5666c193131ab461f153040721728b48730", + "size": "36396804" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-aarch64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:24f85aa778e1605098a13ff7bd29d5760767faf012705c8915cb08b32cad0206", - "size": "35442104" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-aarch64-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-aarch64-linux-gnu.tar.gz", + "checksum": "SHA-256:ecbd53ba28cf24301be8260249bfcfb60567f938f4402797617c8a0fc170dc7d", + "size": "35457879" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-arm-linux-gnueabi.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:c73e43038b6d50374cd0ee714370ce748189e0b00404d581babd2bb0115c4785", - "size": "31260410" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-arm-linux-gnueabi.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-arm-linux-gnueabi.tar.gz", + "checksum": "SHA-256:c0895e88797089fd6b16e1cb986c5c85a880e0e8dc03bde1016c7771bc10ddba", + "size": "31288407" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-i586-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:dc7b8aad0fb1c6a1abfdb8dff4f08221ea08a0f28fb837f181969ac1174d4dc6", - "size": "35067894" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-i586-linux-gnu.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-i586-linux-gnu.tar.gz", + "checksum": "SHA-256:64ffefb7625edae77a03a13fd9bd07db088dec9d145eb1124de66f11510f7558", + "size": "35094067" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-x86_64-apple-darwin21.1.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-x86_64-apple-darwin21.1.tar.gz", - "checksum": "SHA-256:398c429cfe696bad01d636c5488cadc87b20471c1b5ed02c60eee5ef2a775c93", - "size": "54992785" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-x86_64-apple-darwin24.5.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-x86_64-apple-darwin24.5.tar.gz", + "checksum": "SHA-256:8341493abc87e6ae468f4eda16c768b2ddb20c98336e1c491a3801ad823680ae", + "size": "45646032" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-aarch64-apple-darwin21.1.tar.gz", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-aarch64-apple-darwin21.1.tar.gz", - "checksum": "SHA-256:b6d85c0d76d653bb55f9d06b0cd509eab7e99db541c88b8c849c64827e9d74a9", - "size": "43538967" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-aarch64-apple-darwin24.5.tar.gz", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-aarch64-apple-darwin24.5.tar.gz", + "checksum": "SHA-256:251e3be9c9436d9ab7fee6c05519fd816a05e63bd47495e24ea4e354881a851c", + "size": "39809369" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-i686-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-i686-w64-mingw32.zip", - "checksum": "SHA-256:f748d6b65fdf66733b82e12d0d85a05e3134122416280379df129cfebe2aa4b2", - "size": "32189419" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-i686-w64-mingw32.zip", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-i686-w64-mingw32.zip", + "checksum": "SHA-256:8fc9fa6a934523b6ad6e787cf1664d48496bae456fd85ea7589e3684ce3bbbe5", + "size": "32824604" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/xtensa-esp-elf-gdb-16.2_20250324-x86_64-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-gdb-16.2_20250324-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:e970fc3ec8a1d0acee2432e91e0a01b348613a0425aacfa981b2fc505fe920cc", - "size": "32290997" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/xtensa-esp-elf-gdb-16.3_20250913-x86_64-w64-mingw32.zip", + "archiveFileName": "xtensa-esp-elf-gdb-16.3_20250913-x86_64-w64-mingw32.zip", + "checksum": "SHA-256:99a2243b9a75bbac95a672cc3ab4b36013429ab5b4583e7a28339e3015a3fdfa", + "size": "33835839" } ] }, { "name": "riscv32-esp-elf-gcc", - "version": "esp-14.2.0_20241119", + "version": "esp-14.2.0_20251107", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-x86_64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:a16942465d33c7f0334c16e83bc6feb62e06eeb79cf19099293480bb8d48c0cd", - "size": "593721156" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-x86_64-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-x86_64-linux-gnu.tar.gz", + "checksum": "SHA-256:051403b578f5c76ab5fbadb5171ec580df7695b16657a97d27e728b92ba64854", + "size": "598664912" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-aarch64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:22486233d0e0fd58a54ae453b701f195f1432fc6f2e17085b9d6c8d5d9acefb7", - "size": "587879927" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-aarch64-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-aarch64-linux-gnu.tar.gz", + "checksum": "SHA-256:b7c7ebcd109c7bcf173f541e0974d4bb563f577acfccb9e60da502be73bc6070", + "size": "592595534" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-arm-linux-gnueabi.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:27a72d5d96cdb56dae2a1da5dfde1717c18a8c1f9a1454c8e34a8bd34abe662d", - "size": "586531522" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-arm-linux-gnueabi.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-arm-linux-gnueabi.tar.gz", + "checksum": "SHA-256:c6f02999b34dcfef6b035f727ed9505717dba33285cde7c62798f4e92d650fb7", + "size": "591433574" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-i586-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:b7bd6e4cd53a4c55831d48e96a3d500bfffb091bec84a30bc8c3ad687e3eb3a2", - "size": "597070471" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-i586-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-i586-linux-gnu.tar.gz", + "checksum": "SHA-256:7385b2537f62cf77526a37f9b229f9deabb22e71ea267c27023d1daac83b40ac", + "size": "602193988" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-x86_64-apple-darwin_signed.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-x86_64-apple-darwin_signed.tar.gz", - "checksum": "SHA-256:5f8b571e1aedbe9f856f3bdeca6600cd5510ccff1ca102c4f001421eda560585", - "size": "602343061" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-x86_64-apple-darwin.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-x86_64-apple-darwin.tar.gz", + "checksum": "SHA-256:81bf86f4bc07e3355ff0fc5f5699c0880d450d8ec74cdd6c734747d721e73994", + "size": "608972763" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-aarch64-apple-darwin_signed.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-aarch64-apple-darwin_signed.tar.gz", - "checksum": "SHA-256:a7276042a7eb2d33c2dff7167539e445c32c07d43a2c6827e86d035642503e0b", - "size": "578521565" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-aarch64-apple-darwin.tar.gz", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-aarch64-apple-darwin.tar.gz", + "checksum": "SHA-256:c87e17eced80e82d971508f9a01ce602155e2b801a7e7dcbe648ace0f722fe9d", + "size": "584629596" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-i686-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-i686-w64-mingw32.zip", - "checksum": "SHA-256:54193a97bd75205678ead8d11f00b351cfa3c2a6e5ab5d966341358b9f9422d7", - "size": "672055172" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-i686-w64-mingw32.zip", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-i686-w64-mingw32.zip", + "checksum": "SHA-256:7670128df99adbdcbc99ebbdccda19347daf2fd191aab1eb22c24ae1c4d77226", + "size": "690936765" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20241119/riscv32-esp-elf-14.2.0_20241119-x86_64-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-14.2.0_20241119-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:24c8407fa467448d394e0639436a5ede31caf1838e35e8435e19df58ebed438c", - "size": "677812937" + "url": "https://github.com/espressif/crosstool-NG/releases/download/esp-14.2.0_20251107/riscv32-esp-elf-14.2.0_20251107-x86_64-w64-mingw32.zip", + "archiveFileName": "riscv32-esp-elf-14.2.0_20251107-x86_64-w64-mingw32.zip", + "checksum": "SHA-256:373abecd1cdfd480b09b8659e319e636064f99fec46f635a05c5413e5f009c05", + "size": "697522467" } ] }, { "name": "riscv32-esp-elf-gdb", - "version": "16.2_20250324", + "version": "16.3_20250913", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-x86_64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:f9b172d8d72d0a1e2b0b80127df29263a0cb0d0c4e998e09c27031bfac09f3ec", - "size": "36528201" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-x86_64-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-x86_64-linux-gnu.tar.gz", + "checksum": "SHA-256:4e3cf8b7d11c7a2d1b50f40b1c50c0671dfe7eb13782c27c8a8cfdc8548bcdd4", + "size": "36557187" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-aarch64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:68bb6a85fb58b8a738f799e8fb4fa1f56cfeffc4de803ceb03c8a33cb2cd919d", - "size": "35643464" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-aarch64-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-aarch64-linux-gnu.tar.gz", + "checksum": "SHA-256:8f1f4f24fa534c76ed9d71efffbf728cc30169e911742d7bd67dd0fdcf5f3ae3", + "size": "35664185" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-arm-linux-gnueabi.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:673038ab9fb2b7391ff9252824194e3b9e40668efe9ce54d1e582a9d6c51f04a", - "size": "32154574" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-arm-linux-gnueabi.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-arm-linux-gnueabi.tar.gz", + "checksum": "SHA-256:ac4fc85e3daf190b21598ec468933dc2659033580715560f45827da25e15b285", + "size": "32183532" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-i586-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:62f05d5fe08145b25e423dd0b3f1ae260be99abf5462b8cfd918bf2231e26e30", - "size": "35410891" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-i586-linux-gnu.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-i586-linux-gnu.tar.gz", + "checksum": "SHA-256:905dcd78558d7d559a95dc1eacc4572ea908be4ae6b1c937ea63a98df4482ca9", + "size": "35438945" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-x86_64-apple-darwin21.1.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-x86_64-apple-darwin21.1.tar.gz", - "checksum": "SHA-256:63ae12cfbab648e2d2ca7a700a0c615c4f36a6fbe6876c11ba108115ee0d60f2", - "size": "55359246" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-x86_64-apple-darwin24.5.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-x86_64-apple-darwin24.5.tar.gz", + "checksum": "SHA-256:2d5e5efead0b189e13cfe2670ca9d6d5965378ef3632d0b163a14f2f0536c274", + "size": "45892529" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-aarch64-apple-darwin21.1.tar.gz", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-aarch64-apple-darwin21.1.tar.gz", - "checksum": "SHA-256:bfbe49774f839020cef988537da0a06896dfe4a382674c62285361ed9bd4aee3", - "size": "43926592" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-aarch64-apple-darwin24.5.tar.gz", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-aarch64-apple-darwin24.5.tar.gz", + "checksum": "SHA-256:92771492084746fd22521c7c5b52bf1ed6dd86ef3cafe60e771bbdb4f0943f5a", + "size": "40145407" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-i686-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-i686-w64-mingw32.zip", - "checksum": "SHA-256:e8b84eec990ff514729b3770edf2b543f36670f43663ce0c3b624fb4884812ca", - "size": "32914955" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-i686-w64-mingw32.zip", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-i686-w64-mingw32.zip", + "checksum": "SHA-256:c6a36c469d3b76e2b442be207814f7c3f71f21faf6faab4dd33fdedd56d89c01", + "size": "33531234" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.2_20250324/riscv32-esp-elf-gdb-16.2_20250324-x86_64-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-gdb-16.2_20250324-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:37c79178900c19ca7487c26af4b5ad6b0d3f34683bd0e9c2ddd39038c999e429", - "size": "32667353" + "url": "https://github.com/espressif/binutils-gdb/releases/download/esp-gdb-v16.3_20250913/riscv32-esp-elf-gdb-16.3_20250913-x86_64-w64-mingw32.zip", + "archiveFileName": "riscv32-esp-elf-gdb-16.3_20250913-x86_64-w64-mingw32.zip", + "checksum": "SHA-256:32e79cb43b40f3b256193139b1fefd2782e3eaf82ee317b757ec8ba18b35159d", + "size": "34196400" } ] }, { "name": "openocd-esp32", - "version": "v0.12.0-esp32-20250422", + "version": "v0.12.0-esp32-20250707", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-amd64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-amd64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:eb1fa9b21c65b45a2200af6dcc2914e32335d37b6dbbd181778dcc0dc025e70a", - "size": "2445546" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-amd64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-amd64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:766293bd7a08900d3536f87a0a7ade960f07266f16e4147f95ca5ce4e15d4c5d", + "size": "2489724" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-arm64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-arm64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:f70334a9b12a75b4d943e09fa5db30973037c39dbb54d6fa9f1a7118228b3d1c", - "size": "2330926" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-arm64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-arm64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:34b6883c372444b49950893b2fc0101aefd10d404a88ef72c97e80199f8544d3", + "size": "2371243" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-armel-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-armel-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:4ac34d6fd1af86aeda87c8318732f8d691c300c285c7fd2f5037c432c63fbbb3", - "size": "2470732" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-armel-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-armel-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:fd48492cf3ee16577c661fdccc14c349d34a9ab93aac5039ddf72332d4f4b70b", + "size": "2517680" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-macos-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-macos-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:9186a7a06304c6d9201cbce4ee3c7099b393bf8d329cda17a68874f92308f6ce", - "size": "2548730" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-macos-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-macos-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:6267be53892a76d535938a1b044b685adc7d292f090447e8a3e3d0f0996474d1", + "size": "2585348" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-macos-arm64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-macos-arm64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:2cc39318d52f393233ff1f777871aebe5b97b3fbad29556a238489263401b774", - "size": "2593819" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-macos-arm64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-macos-arm64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:150e938ac48a6ee031ddbc8b31043bc7f2073ab2ee4896b658918d35899673c3", + "size": "2628741" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-win32-0.12.0-esp32-20250422.zip", - "archiveFileName": "openocd-esp32-win32-0.12.0-esp32-20250422.zip", - "checksum": "SHA-256:ecb4f8533fa9098d10000f5f7e8b8eaa8591015b824b481078ddb2b37e7aa6f2", - "size": "2988859" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-win32-0.12.0-esp32-20250707.zip", + "archiveFileName": "openocd-esp32-win32-0.12.0-esp32-20250707.zip", + "checksum": "SHA-256:666274b04af7f36b430b6d063006051c37b8635b5175735ad5af07a1fbc6f486", + "size": "3034680" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-win64-0.12.0-esp32-20250422.zip", - "archiveFileName": "openocd-esp32-win64-0.12.0-esp32-20250422.zip", - "checksum": "SHA-256:e9eae8e1a8d0e030cd81dcb08394a9137cb7338a6211dfabcdbdfb37b58c5a23", - "size": "2988858" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-win64-0.12.0-esp32-20250707.zip", + "archiveFileName": "openocd-esp32-win64-0.12.0-esp32-20250707.zip", + "checksum": "SHA-256:5186ba3f7ee29fb6ab68a4ed7bb417211bad76ecdcdf9280a9187ebfd549a3c1", + "size": "3034680" } ] }, { "name": "esptool_py", - "version": "5.0.2", + "version": "5.1.0", "systems": [ { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-linux-aarch64.tar.gz", - "archiveFileName": "esptool-v5.0.2-linux-aarch64.tar.gz", - "checksum": "SHA-256:0c1fa4f5e96f715133fa65572063fa65bd120cd941b667cecd3360436a62b97b", - "size": "57815944" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-aarch64.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-aarch64.tar.gz", + "checksum": "SHA-256:d2b60d4570cd4919b87eddcbeaab2e0411548b5aab865c234aed8ecc8e5403ac", + "size": "56292242" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-linux-amd64.tar.gz", - "archiveFileName": "esptool-v5.0.2-linux-amd64.tar.gz", - "checksum": "SHA-256:519e0015872d527bdca850b18575d4f635fa88ccdffe47a14c99a80a90b780c5", - "size": "100787554" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-amd64.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-amd64.tar.gz", + "checksum": "SHA-256:49d572d50f6b1f089d1d81d3bd3bd357fbcc40f4f8fd4874f2dc51ad534abb01", + "size": "57690602" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-linux-armv7.tar.gz", - "archiveFileName": "esptool-v5.0.2-linux-armv7.tar.gz", - "checksum": "SHA-256:8df698d46a64b0b4a36d2a5bbd6bae58f81ca0a5e6451cd3f2d69a33340cc0f1", - "size": "53046401" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-armv7.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-armv7.tar.gz", + "checksum": "SHA-256:e22ecb0293fe73c80d0a5fd05873f9ea49a68985b16991cf5980d2b90c8c7276", + "size": "53396928" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-macos-amd64.tar.gz", - "archiveFileName": "esptool-v5.0.2-macos-amd64.tar.gz", - "checksum": "SHA-256:5c27295975515b97a9280f46845bd3acd5fc9e6a0583cfe03efa66cc50195ab0", - "size": "59619952" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-macos-amd64.tar.gz", + "archiveFileName": "esptool-v5.1.0-macos-amd64.tar.gz", + "checksum": "SHA-256:c485511e0906cb1e0277c5eecff1c4a77b89d76d0c940b685dc9fce2fad4b242", + "size": "59088390" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-macos-arm64.tar.gz", - "archiveFileName": "esptool-v5.0.2-macos-arm64.tar.gz", - "checksum": "SHA-256:93f0d9ef169f9bc6e32ed6f381977f63a5df7483b8002a5676dddf055bdbf775", - "size": "56344929" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-macos-arm64.tar.gz", + "archiveFileName": "esptool-v5.1.0-macos-arm64.tar.gz", + "checksum": "SHA-256:5d5aab5b64b10dc5001cfa96b5bfa48393ae561e6d797c41a1fdd3f5d3843d03", + "size": "56092727" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-windows-amd64.zip", - "archiveFileName": "esptool-v5.0.2-windows-amd64.zip", - "checksum": "SHA-256:1caef993a16c5915714a0da772d93f2e3239f316c06223981b262b838287268c", - "size": "59097582" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-windows-amd64.zip", + "archiveFileName": "esptool-v5.1.0-windows-amd64.zip", + "checksum": "SHA-256:f68a8f7728adfc59cd60f9424928199e76eac66372c7bdc23898aa32753a437a", + "size": "59936293" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.2/esptool-v5.0.2-windows-amd64.zip", - "archiveFileName": "esptool-v5.0.2-windows-amd64.zip", - "checksum": "SHA-256:1caef993a16c5915714a0da772d93f2e3239f316c06223981b262b838287268c", - "size": "59097582" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-windows-amd64.zip", + "archiveFileName": "esptool-v5.1.0-windows-amd64.zip", + "checksum": "SHA-256:f68a8f7728adfc59cd60f9424928199e76eac66372c7bdc23898aa32753a437a", + "size": "59936293" } ] }, { - "version": "3.0.0-gnu12-dc7f933", + "version": "4.0.2-db0513a", "name": "mklittlefs", "systems": [ { - "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:fc56e389383749e4cf4fab0fcf75cc0ebc41e59383caf6c2eff1c3d9794af200", - "size": "44651" + "host": "aarch64-apple-darwin", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/aarch64-apple-darwin-mklittlefs-db0513a.tar.gz", + "archiveFileName": "aarch64-apple-darwin-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:9a271456c9caf9129a8881b1d10474dcb15d25ce243dff557fdf4cfaae74f1e4", + "size": "53793" }, { - "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:52b642dd0545eb3bd8dfb75dde6601df21700e4867763fd2696274be279294c5", - "size": "37211" + "host": "aarch64-linux-gnu", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/aarch64-linux-gnu-mklittlefs-db0513a.tar.gz", + "archiveFileName": "aarch64-linux-gnu-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:281e951ccaf9f637142198bde1ca97be9c330a5ad3b15e9096016a521d459341", + "size": "52890" }, { - "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:7886051d8ccc54aed0af2e7cdf6ff992bb51638df86f3b545955697720b6d062", - "size": "48033" + "host": "arm-linux-gnueabihf", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/arm-linux-gnueabihf-mklittlefs-db0513a.tar.gz", + "archiveFileName": "arm-linux-gnueabihf-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:49e4bba4ef6ff338c301a5d264badd86f3b494e48ea278e8d06a435b5da04f81", + "size": "45403" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "archiveFileName": "i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "checksum": "SHA-256:43740db30ce451454f2337331f10ab4ed41bd83dbf0fa0cb4387107388b59f42", - "size": "332655" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/i686-w64-mingw32-mklittlefs-db0513a.zip", + "archiveFileName": "i686-w64-mingw32-mklittlefs-db0513a.zip", + "checksum": "SHA-256:47d7d9b70397ee8dd0b858d3cc0f01db4d8a03a031652f788d81700c74f107cd", + "size": "489405" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:e3edd5e05b70db3c7df6b9d626558348ad04804022fe955c799aeb51808c7dc3", - "size": "362608" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-apple-darwin-mklittlefs-db0513a.tar.gz", + "archiveFileName": "x86_64-apple-darwin-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:90f20aa1c5f2d91bbe00821943ef5a291160ce4bc9077cc76f6350b47d978755", + "size": "58977" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:66e84dda0aad747517da3785125e05738a540948aab2b7eaa02855167a1eea53", - "size": "46778" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-linux-gnu-mklittlefs-db0513a.tar.gz", + "archiveFileName": "x86_64-linux-gnu-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:7a70428b7089bf1c9d481b0e070e99cd8a430d37e197b7d3db64f24ba8891508", + "size": "54000" }, { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "archiveFileName": "x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "checksum": "SHA-256:2e319077491f8e832e96eb4f2f7a70dd919333cee4b388c394e0e848d031d542", - "size": "345132" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-w64-mingw32-mklittlefs-db0513a.zip", + "archiveFileName": "x86_64-w64-mingw32-mklittlefs-db0513a.zip", + "checksum": "SHA-256:e99dbfcf2b808a2020254764f06e336aa6a4d253ab09bcabe01399fcd95d9ab8", + "size": "452707" } ] }, diff --git a/platform.txt b/platform.txt index 82560190c8f..4bab878172a 100644 --- a/platform.txt +++ b/platform.txt @@ -1,5 +1,5 @@ name=ESP32 Arduino -version=3.3.0 +version=3.3.4 tools.esp32-arduino-libs.path={runtime.platform.path}/tools/esp32-arduino-libs tools.esp32-arduino-libs.path.windows={runtime.platform.path}\tools\esp32-arduino-libs @@ -27,8 +27,8 @@ tools.gen_insights_pkg.cmd.windows="{runtime.platform.path}\tools\gen_insights_p compiler.path={tools.{build.tarch}-esp-elf-gcc.path}/bin/ compiler.prefix={build.tarch}-{build.target}-elf- -compiler.sdk.path={tools.esp32-arduino-libs.path}/{build.mcu} -compiler.sdk.path.windows={tools.esp32-arduino-libs.path}\{build.mcu} +compiler.sdk.path={tools.esp32-arduino-libs.path}/{build.chip_variant} +compiler.sdk.path.windows={tools.esp32-arduino-libs.path}\{build.chip_variant} # EXPERIMENTAL feature: optimization flags # - this is alpha and may be subject to change without notice @@ -85,6 +85,7 @@ build.extra_flags.esp32c6=-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT={build. build.extra_flags.esp32h2=-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot} build.extra_flags.esp32p4=-DARDUINO_USB_MODE={build.usb_mode} -DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot} -DARDUINO_USB_MSC_ON_BOOT={build.msc_on_boot} -DARDUINO_USB_DFU_ON_BOOT={build.dfu_on_boot} build.extra_flags.esp32c5=-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot} +build.extra_flags.esp32c61=-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT={build.cdc_on_boot} # This can be overriden in boards.txt build.zigbee_mode= @@ -105,6 +106,7 @@ build.event_core= build.extra_flags=-DARDUINO_HOST_OS="{runtime.os}" -DARDUINO_FQBN="{build.fqbn}" -DESP32=ESP32 -DCORE_DEBUG_LEVEL={build.code_debug} {build.loop_core} {build.event_core} {build.defines} {build.extra_flags.{build.mcu}} {build.zigbee_mode} build.extra_libs= build.memory_type={build.boot}_qspi +build.chip_variant={build.mcu} # Custom build options build.opt.name=build_opt.h @@ -180,6 +182,10 @@ recipe.hooks.objcopy.postobjcopy.2.pattern.windows=cmd /c if exist "{build.path} recipe.hooks.objcopy.postobjcopy.3.pattern_args=--chip {build.mcu} merge-bin -o "{build.path}/{build.project_name}.merged.bin" --pad-to-size {build.flash_size} --flash-mode keep --flash-freq keep --flash-size keep {build.bootloader_addr} "{build.path}/{build.project_name}.bootloader.bin" 0x8000 "{build.path}/{build.project_name}.partitions.bin" 0xe000 "{runtime.platform.path}/tools/partitions/boot_app0.bin" 0x10000 "{build.path}/{build.project_name}.bin" recipe.hooks.objcopy.postobjcopy.3.pattern="{tools.esptool_py.path}/{tools.esptool_py.cmd}" {recipe.hooks.objcopy.postobjcopy.3.pattern_args} +# Generate flash_args file +recipe.hooks.objcopy.postobjcopy.4.pattern=/usr/bin/env bash -c "echo '--flash-mode {build.flash_mode} --flash-freq {build.img_freq} --flash-size {build.flash_size}' > '{build.path}/flash_args' && echo '{build.bootloader_addr} {build.project_name}.bootloader.bin' >> '{build.path}/flash_args' && echo '0x8000 {build.project_name}.partitions.bin' >> '{build.path}/flash_args' && echo '0xe000 boot_app0.bin' >> '{build.path}/flash_args' && echo '0x10000 {build.project_name}.bin' >> '{build.path}/flash_args'" +recipe.hooks.objcopy.postobjcopy.4.pattern.windows=cmd /c echo --flash-mode {build.flash_mode} --flash-freq {build.img_freq} --flash-size {build.flash_size} > "{build.path}\flash_args" && echo {build.bootloader_addr} {build.project_name}.bootloader.bin >> "{build.path}\flash_args" && echo 0x8000 {build.project_name}.partitions.bin >> "{build.path}\flash_args" && echo 0xe000 boot_app0.bin >> "{build.path}\flash_args" && echo 0x10000 {build.project_name}.bin >> "{build.path}\flash_args" + ## Save bin recipe.output.tmp_file={build.project_name}.bin recipe.output.save_file={build.project_name}.{build.variant}.bin @@ -352,6 +358,6 @@ tools.esptool_py_app_only.upload.protocol=serial tools.esptool_py_app_only.upload.params.verbose= tools.esptool_py_app_only.upload.params.quiet= -tools.esptool_py_app_only.upload.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default_reset --after hard_reset write_flash --flash_mode {build.flash_mode} --flash_freq {build.flash_freq} --flash_size {build.flash_size} {build.flash_offset} "{build.path}/{build.project_name}.bin" {upload.extra_flags} +tools.esptool_py_app_only.upload.pattern_args=--chip {build.mcu} --port "{serial.port}" --baud {upload.speed} {upload.flags} --before default-reset --after hard-reset write-flash --flash-mode {build.flash_mode} --flash-freq {build.flash_freq} --flash-size {build.flash_size} {build.flash_offset} "{build.path}/{build.project_name}.bin" {upload.extra_flags} tools.esptool_py_app_only.upload.pattern="{path}/{cmd}" {tools.esptool_py_app_only.upload.pattern_args} diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000000..449dabc4758 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,22 @@ +import pytest +import os + + +def pytest_addoption(parser): + parser.addoption("--wifi-password", action="store", default=None, help="Wi-Fi password.") + parser.addoption("--wifi-ssid", action="store", default=None, help="Wi-Fi SSID.") + + +@pytest.fixture(scope="session") +def wifi_ssid(request): + return request.config.getoption("--wifi-ssid") + + +@pytest.fixture(scope="session") +def wifi_pass(request): + return request.config.getoption("--wifi-password") + + +@pytest.fixture(scope="session") +def ci_job_id(request): + return os.environ.get("CI_JOB_ID") diff --git a/tests/performance/coremark/ci.json b/tests/performance/coremark/ci.json deleted file mode 100644 index accee2b2135..00000000000 --- a/tests/performance/coremark/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/coremark/ci.yml b/tests/performance/coremark/ci.yml new file mode 100644 index 00000000000..a5625fc9fa1 --- /dev/null +++ b/tests/performance/coremark/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/coremark/test_coremark.py b/tests/performance/coremark/test_coremark.py index befd7c3a1c9..b4eb35e09b5 100644 --- a/tests/performance/coremark/test_coremark.py +++ b/tests/performance/coremark/test_coremark.py @@ -45,13 +45,14 @@ def test_coremark(dut, request): results = {"coremark": {"runs": runs, "cores": cores, "avg_score": avg_score}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_coremark" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_coremark" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/fibonacci/ci.json b/tests/performance/fibonacci/ci.json deleted file mode 100644 index accee2b2135..00000000000 --- a/tests/performance/fibonacci/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/fibonacci/ci.yml b/tests/performance/fibonacci/ci.yml new file mode 100644 index 00000000000..a5625fc9fa1 --- /dev/null +++ b/tests/performance/fibonacci/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/fibonacci/test_fibonacci.py b/tests/performance/fibonacci/test_fibonacci.py index cf560d9691c..a1658a4d4fd 100644 --- a/tests/performance/fibonacci/test_fibonacci.py +++ b/tests/performance/fibonacci/test_fibonacci.py @@ -67,13 +67,14 @@ def test_fibonacci(dut, request): results = {"fibonacci": {"runs": runs, "fib_n": fib_n, "avg_time": avg_time}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_fibonacci" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_fibonacci" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/linpack_double/ci.json b/tests/performance/linpack_double/ci.json deleted file mode 100644 index accee2b2135..00000000000 --- a/tests/performance/linpack_double/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/linpack_double/ci.yml b/tests/performance/linpack_double/ci.yml new file mode 100644 index 00000000000..a5625fc9fa1 --- /dev/null +++ b/tests/performance/linpack_double/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/linpack_double/test_linpack_double.py b/tests/performance/linpack_double/test_linpack_double.py index 0a6e2f90ef3..246281e3d0d 100644 --- a/tests/performance/linpack_double/test_linpack_double.py +++ b/tests/performance/linpack_double/test_linpack_double.py @@ -48,13 +48,14 @@ def test_linpack_double(dut, request): results = {"linpack_double": {"runs": runs, "avg_score": avg_score, "min_score": min_score, "max_score": max_score}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_linpack_double" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_linpack_double" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/linpack_float/ci.json b/tests/performance/linpack_float/ci.json deleted file mode 100644 index accee2b2135..00000000000 --- a/tests/performance/linpack_float/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/linpack_float/ci.yml b/tests/performance/linpack_float/ci.yml new file mode 100644 index 00000000000..a5625fc9fa1 --- /dev/null +++ b/tests/performance/linpack_float/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/linpack_float/test_linpack_float.py b/tests/performance/linpack_float/test_linpack_float.py index d11f6c74136..ec89dc69162 100644 --- a/tests/performance/linpack_float/test_linpack_float.py +++ b/tests/performance/linpack_float/test_linpack_float.py @@ -48,13 +48,14 @@ def test_linpack_float(dut, request): results = {"linpack_float": {"runs": runs, "avg_score": avg_score, "min_score": min_score, "max_score": max_score}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_linpack_float" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_linpack_float" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/psramspeed/ci.json b/tests/performance/psramspeed/ci.json deleted file mode 100644 index 341df103671..00000000000 --- a/tests/performance/psramspeed/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - }, - "requires": [ - "CONFIG_SPIRAM=y" - ] -} diff --git a/tests/performance/psramspeed/ci.yml b/tests/performance/psramspeed/ci.yml new file mode 100644 index 00000000000..7e06eac7cfa --- /dev/null +++ b/tests/performance/psramspeed/ci.yml @@ -0,0 +1,16 @@ +soc_tags: + esp32: + - psram + esp32s2: + - psram + esp32s3: + - octal_psram + esp32c5: + - psram + +platforms: + qemu: false + wokwi: false + +requires: + - CONFIG_SPIRAM=y diff --git a/tests/performance/psramspeed/test_psramspeed.py b/tests/performance/psramspeed/test_psramspeed.py index 9e96e158504..69a130dce9e 100644 --- a/tests/performance/psramspeed/test_psramspeed.py +++ b/tests/performance/psramspeed/test_psramspeed.py @@ -92,13 +92,14 @@ def test_psramspeed(dut, request): results = {"psramspeed": {"runs": runs, "copies": copies, "max_test_size": max_test_size, "results": avg_results}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_psramspeed" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_psramspeed" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/ramspeed/ci.json b/tests/performance/ramspeed/ci.json deleted file mode 100644 index d880ca64dfb..00000000000 --- a/tests/performance/ramspeed/ci.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "fqbn": { - "esp32": [ - "espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=huge_app" - ], - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=huge_app" - ], - "esp32s3": [ - "espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=huge_app" - ] - }, - "platform": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/ramspeed/ci.yml b/tests/performance/ramspeed/ci.yml new file mode 100644 index 00000000000..1b4f093b0fc --- /dev/null +++ b/tests/performance/ramspeed/ci.yml @@ -0,0 +1,11 @@ +fqbn: + esp32: + - espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=huge_app + esp32s2: + - espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=huge_app + esp32s3: + - espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=huge_app + +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/ramspeed/ramspeed.ino b/tests/performance/ramspeed/ramspeed.ino index 776f6540679..5402d2bd8bc 100644 --- a/tests/performance/ramspeed/ramspeed.ino +++ b/tests/performance/ramspeed/ramspeed.ino @@ -234,8 +234,8 @@ void setup() { delay(10); } - void *dest = malloc(MAX_TEST_SIZE); - const void *src = malloc(MAX_TEST_SIZE); + void *dest = heap_caps_malloc(MAX_TEST_SIZE, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + const void *src = heap_caps_malloc(MAX_TEST_SIZE, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); if (!dest || !src) { Serial.println("Memory allocation failed"); diff --git a/tests/performance/ramspeed/test_ramspeed.py b/tests/performance/ramspeed/test_ramspeed.py index dbe1670d329..795a9a0a9ad 100644 --- a/tests/performance/ramspeed/test_ramspeed.py +++ b/tests/performance/ramspeed/test_ramspeed.py @@ -92,13 +92,14 @@ def test_ramspeed(dut, request): results = {"ramspeed": {"runs": runs, "copies": copies, "max_test_size": max_test_size, "results": avg_results}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_ramspeed" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_ramspeed" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/performance/superpi/ci.json b/tests/performance/superpi/ci.json deleted file mode 100644 index accee2b2135..00000000000 --- a/tests/performance/superpi/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - } -} diff --git a/tests/performance/superpi/ci.yml b/tests/performance/superpi/ci.yml new file mode 100644 index 00000000000..a5625fc9fa1 --- /dev/null +++ b/tests/performance/superpi/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/performance/superpi/test_superpi.py b/tests/performance/superpi/test_superpi.py index 0bd7a3477b6..0096cd5478b 100644 --- a/tests/performance/superpi/test_superpi.py +++ b/tests/performance/superpi/test_superpi.py @@ -40,13 +40,14 @@ def test_superpi(dut, request): results = {"superpi": {"runs": runs, "digits": digits, "avg_time": avg_time}} current_folder = os.path.dirname(request.path) + os.makedirs(os.path.join(current_folder, dut.app.target), exist_ok=True) file_index = 0 - report_file = os.path.join(current_folder, "result_superpi" + str(file_index) + ".json") + report_file = os.path.join(current_folder, dut.app.target, "result_superpi" + str(file_index) + ".json") while os.path.exists(report_file): report_file = report_file.replace(str(file_index) + ".json", str(file_index + 1) + ".json") file_index += 1 - with open(report_file, "w") as f: + with open(report_file, "w+") as f: try: f.write(json.dumps(results)) except Exception as e: diff --git a/tests/pytest.ini b/tests/pytest.ini index b507b437727..d7d50e5c8e3 100644 --- a/tests/pytest.ini +++ b/tests/pytest.ini @@ -1,5 +1,8 @@ [pytest] addopts = --embedded-services esp,arduino,wokwi,qemu +junit_family = xunit1 +filterwarnings = + ignore::pytest.PytestExperimentalApiWarning # log related log_cli = True diff --git a/tests/requirements.txt b/tests/requirements.txt index b2bae3b86d0..93d4ec88208 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,8 +1,8 @@ cryptography==44.0.1 --only-binary cryptography pytest-cov==5.0.0 -pytest-embedded-serial-esp==1.12.0 -pytest-embedded-arduino==1.12.0 -pytest-embedded-wokwi==1.12.0 -pytest-embedded-qemu==1.12.0 -esptool==4.8.1 +pytest-embedded-serial-esp==2.4.0 +pytest-embedded-arduino==2.4.0 +pytest-embedded-wokwi==2.4.0 +pytest-embedded-qemu==2.4.0 +esptool==5.1.0 diff --git a/tests/validation/democfg/ci.json b/tests/validation/democfg/ci.json deleted file mode 100644 index cf5c796644e..00000000000 --- a/tests/validation/democfg/ci.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "fqbn": { - "esp32": [ - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio" - ], - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app" - ], - "esp32s3": [ - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app" - ] - }, - "platforms": { - "hardware": true, - "qemu": false, - "wokwi": false - }, - "requires": [ - "CONFIG_SOC_UART_SUPPORTED=y" - ], - "targets": { - "esp32": true, - "esp32c3": false, - "esp32c6": true, - "esp32h2": false, - "esp32s2": true, - "esp32s3": true, - "esp32p4": false - } -} diff --git a/tests/validation/democfg/ci.yml b/tests/validation/democfg/ci.yml new file mode 100644 index 00000000000..d78c4b0f4eb --- /dev/null +++ b/tests/validation/democfg/ci.yml @@ -0,0 +1,25 @@ +fqbn: + esp32: + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio + esp32s2: + - espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app + esp32s3: + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app + +platforms: + hardware: true + qemu: false + wokwi: false + +requires: + - CONFIG_SOC_UART_SUPPORTED=y + +targets: + esp32: true + esp32c3: false + esp32c6: true + esp32h2: false + esp32s2: true + esp32s3: true + esp32p4: false diff --git a/tests/validation/fs/README.md b/tests/validation/fs/README.md new file mode 100644 index 00000000000..4898a287a45 --- /dev/null +++ b/tests/validation/fs/README.md @@ -0,0 +1,44 @@ +# Filesystem Validation Test Suite + +This test suite validates the functionality of different filesystem implementations available for ESP32: SPIFFS, FFat, and LittleFS. + +## Overview + +The test suite uses Unity testing framework to run a comprehensive set of filesystem operations across all three filesystem implementations. Each test is executed for each filesystem to ensure consistent behavior and identify filesystem-specific differences. + +## Tested Filesystems + +- **SPIFFS** (`/spiffs`) - SPI Flash File System +- **FFat** (`/ffat`) - FAT filesystem implementation +- **LittleFS** (`/littlefs`) - Little File System + +## Test Cases + +The suite includes the following test categories: + +- **Basic Operations**: File write, read, append +- **Directory Operations**: Create directories, list files, nested directories +- **File Management**: Rename, remove, exists checks +- **Binary Operations**: Binary data write/read, seek operations +- **Edge Cases**: Empty files, seek edge cases, file truncation +- **Multiple Handles**: Concurrent file operations +- **Space Tracking**: Free space monitoring +- **Error Handling**: Non-existent file operations +- **Large Files**: Operations with larger file sizes +- **Write/Read Patterns**: Sequential and random access patterns +- **Open Files Limit**: Maximum concurrent open files testing + +## Known Filesystem-Specific Behaviors + +### SPIFFS + +- **Directory Support**: SPIFFS does not have true directory support. Opening a directory always returns `true`, and closing it also always returns `true`, regardless of whether the directory actually exists or not. This is a limitation of the SPIFFS implementation. +- **Error Handling**: Some error case tests are skipped for SPIFFS due to different error handling behavior compared to other filesystems. + +### LittleFS + +- **Open Files Limit**: LittleFS does not enforce a maximum open files limit at the same time. The `test_max_open_files_limit()` test is skipped for LittleFS as it doesn't have this constraint. + +### FFat + +- FFat follows standard FAT filesystem behavior and supports all tested operations including proper directory handling and open files limits. diff --git a/tests/validation/fs/fs.ino b/tests/validation/fs/fs.ino new file mode 100644 index 00000000000..1418e914b2c --- /dev/null +++ b/tests/validation/fs/fs.ino @@ -0,0 +1,778 @@ +#include +#include +#include + +#include +#include +#include +#include + +const uint8_t MAX_TEST_OPEN_FILES = 3; // Limit for testing + +class IFileSystem { +public: + virtual const char *name() const = 0; + virtual bool begin(bool formatOnFail) = 0; + virtual void end() = 0; + virtual bool format() = 0; + virtual size_t totalBytes() const = 0; + virtual size_t usedBytes() const = 0; + virtual fs::FS &vfs() = 0; + virtual ~IFileSystem() {} +}; + +/** + * The VFSImpl interface does not expose all methods needed for testing (such as usedBytes, format, etc.), + * so we wrap the concrete implementations to provide a unified interface. + */ +template class WrappedFS : public IFileSystem { +public: + WrappedFS(Impl &impl, const char *basePath, const char *label, uint8_t maxOpen) : impl_(&impl), basePath_(basePath), label_(label), maxOpen_(maxOpen) {} + + const char *name() const override { + return label_; + } + + bool begin(bool formatOnFail) override { + return impl_->begin(formatOnFail, basePath_, maxOpen_, label_); + } + void end() override { + impl_->end(); + } + bool format() override { + return impl_->format(); + } + size_t totalBytes() const override { + return impl_->totalBytes(); + } + size_t usedBytes() const override { + return impl_->usedBytes(); + } + fs::FS &vfs() override { + return *impl_; + } + +private: + Impl *impl_; + const char *basePath_; + const char *label_; + uint8_t maxOpen_; +}; + +// Concrete wrappers (labels must match the CSV) +static WrappedFS FS_SPIFFS(SPIFFS, "/spiffs", "spiffs", MAX_TEST_OPEN_FILES); +static WrappedFS FS_FFAT(FFat, "/ffat", "fat", MAX_TEST_OPEN_FILES); +static WrappedFS FS_LFS(LittleFS, "/littlefs", "littlefs", MAX_TEST_OPEN_FILES); +static IFileSystem *gFS = nullptr; + +void setUp() { + TEST_ASSERT_NOT_NULL_MESSAGE(gFS, "Internal: gFS not set"); + // Try to mount with format on fail - this handles both fresh mount and remount cases + bool mounted = gFS->begin(true); + TEST_ASSERT_TRUE_MESSAGE(mounted, "Mount failed"); +} + +void tearDown() { + gFS->end(); +} + +void test_info_sanity() { + size_t tot = gFS->totalBytes(); + size_t used = gFS->usedBytes(); + TEST_ASSERT_TRUE_MESSAGE(tot > 0, "totalBytes() is zero"); + TEST_ASSERT_TRUE_MESSAGE(tot >= used, "usedBytes() > totalBytes()"); +} + +void test_basic_write_and_read() { + auto &V = gFS->vfs(); + { + // write and overwrite + for (int i = 0; i < 3; ++i) { + File f = V.open("/t.txt", FILE_WRITE); + TEST_ASSERT_EQUAL_INT(0, (int)f.size()); + TEST_ASSERT_TRUE_MESSAGE(f, "open WRITE failed"); + TEST_ASSERT_EQUAL_INT(5, (int)f.print("hello")); + f.close(); + } + } + { + // read back + File f = V.open("/t.txt", FILE_READ); + TEST_ASSERT_GREATER_THAN(0, (int)f.size()); + TEST_ASSERT_TRUE_MESSAGE(f, "open READ failed"); + String s = f.readString(); + f.close(); + TEST_ASSERT_EQUAL_STRING("hello", s.c_str()); + } +} + +void test_append_behavior() { + auto &V = gFS->vfs(); + { + File f = V.open("/append.txt", FILE_APPEND); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_GREATER_OR_EQUAL(0, (int)f.size()); + f.println("line1"); + f.close(); + } + { + File f = V.open("/append.txt", FILE_APPEND); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_GREATER_THAN(0, (int)f.size()); + f.println("line2"); + f.close(); + } + { + File f = V.open("/append.txt", FILE_READ); + TEST_ASSERT_TRUE(f); + String s = f.readString(); + f.close(); + TEST_ASSERT_NOT_EQUAL(-1, s.indexOf("line1")); + TEST_ASSERT_NOT_EQUAL(-1, s.indexOf("line2")); + } + + TEST_ASSERT_TRUE(V.remove("/append.txt")); +} + +void test_dir_ops_and_list() { + auto &V = gFS->vfs(); + const char *fileBasePath = "/dir/a/b"; + const int numFiles = 5; + { + // create nested directories + TEST_ASSERT_TRUE_MESSAGE(V.mkdir("/dir"), "mkdir /dir failed"); + TEST_ASSERT_TRUE_MESSAGE(V.mkdir("/dir/a"), "mkdir /dir/a failed"); + TEST_ASSERT_TRUE_MESSAGE(V.mkdir(fileBasePath), "mkdir /dir/a/b failed"); + } + + { + for (int i = 0; i < numFiles; ++i) { + String path = String(fileBasePath) + String("/file") + String(i) + String(".txt"); + File f = V.open(path.c_str(), FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(f, ("open " + path + " failed").c_str()); + f.print("data:" + String(i)); + f.close(); + } + } + + { + File d = V.open(fileBasePath); + TEST_ASSERT_TRUE_MESSAGE(d && d.isDirectory(), "open(/dir/a/b) not a directory"); + + auto getExpectedFile = [fileBasePath](int i) -> std::pair { + return {String(fileBasePath) + "/file" + String(i) + ".txt", "data:" + String(i)}; + }; + + bool found[numFiles] = {false}; + int count = 0; + + while (true) { + File e = d.openNextFile(); + if (!e) { + break; + } + + String path = e.path(); + String content = e.readString(); + bool matched = false; + + for (int i = 0; i < numFiles; ++i) { + if (!found[i]) { + auto [expectedPath, expectedContent] = getExpectedFile(i); + if (path == expectedPath) { + TEST_ASSERT_EQUAL_STRING_MESSAGE(expectedContent.c_str(), content.c_str(), "File content mismatch"); + found[i] = true; + matched = true; + break; + } + } + } + + TEST_ASSERT_TRUE_MESSAGE(matched, ("Unexpected file found: " + path).c_str()); + count++; + e.close(); + } + + d.close(); + TEST_ASSERT_EQUAL_INT_MESSAGE(numFiles, count, "File count mismatch in directory listing"); + + for (int i = 0; i < numFiles; ++i) { + auto [expectedPath, _] = getExpectedFile(i); + TEST_ASSERT_TRUE_MESSAGE(found[i], ("Expected file not found: " + expectedPath).c_str()); + } + } +} + +void test_rename_and_remove() { + auto &V = gFS->vfs(); + { + File f = V.open("/t.txt", FILE_WRITE); + TEST_ASSERT_TRUE(f); + f.print("x"); + f.close(); + } + TEST_ASSERT_TRUE(V.rename("/t.txt", "/t2.txt")); + TEST_ASSERT_TRUE(V.exists("/t2.txt")); + TEST_ASSERT_TRUE(!V.exists("/t.txt")); + TEST_ASSERT_TRUE(V.remove("/t2.txt")); + TEST_ASSERT_TRUE(!V.exists("/t2.txt")); +} + +void test_binary_write_and_seek() { + auto &V = gFS->vfs(); + { + File f = V.open("/bin.dat", FILE_WRITE); + TEST_ASSERT_TRUE(f); + for (int i = 0; i < 256; ++i) { + f.write((uint8_t)i); + } + f.close(); + } + { + File f = V.open("/bin.dat", FILE_READ); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(256, (uint32_t)f.size()); + TEST_ASSERT_TRUE(f.seek(123)); + int b = f.read(); + f.close(); + TEST_ASSERT_EQUAL_INT(123, b); + } +} + +void test_binary_incremental_with_size_tracking() { + auto &V = gFS->vfs(); + const char *path = "/bin_inc.dat"; + const int chunkSize = 64; + const int numChunks = 8; + uint8_t writeBuffer[chunkSize]; + uint8_t readBuffer[chunkSize]; + + // Initialize write buffer with pattern + for (int i = 0; i < chunkSize; ++i) { + writeBuffer[i] = (uint8_t)(i & 0xFF); + } + + { + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + size_t expectedSize = 0; + + // Write chunks incrementally and verify position after each write + // Note: size() may not update until file is closed/flushed on some filesystems + for (int chunk = 0; chunk < numChunks; ++chunk) { + size_t posBefore = f.position(); + TEST_ASSERT_EQUAL_UINT32(expectedSize, (uint32_t)posBefore); + + size_t written = f.write(writeBuffer, chunkSize); + TEST_ASSERT_EQUAL_UINT32(chunkSize, (uint32_t)written); + + expectedSize += chunkSize; + + // Verify position advances correctly (more reliable than size during write) + size_t posAfter = f.position(); + TEST_ASSERT_EQUAL_UINT32(expectedSize, (uint32_t)posAfter); + + // Flush to ensure data is written (some filesystems need this for accurate size) + f.flush(); + } + + f.close(); + + // Verify final file size + File check = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(check); + TEST_ASSERT_EQUAL_UINT32(expectedSize, (uint32_t)check.size()); + check.close(); + } + + { + // Read back and verify content + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(numChunks * chunkSize, (uint32_t)f.size()); + + for (int chunk = 0; chunk < numChunks; ++chunk) { + size_t sizeBefore = f.size(); + size_t posBefore = f.position(); + + size_t read = f.read(readBuffer, chunkSize); + TEST_ASSERT_EQUAL_UINT32(chunkSize, (uint32_t)read); + + // Size should not change during read + size_t sizeAfter = f.size(); + TEST_ASSERT_EQUAL_UINT32(sizeBefore, (uint32_t)sizeAfter); + + // Position should advance + size_t posAfter = f.position(); + TEST_ASSERT_EQUAL_UINT32(posBefore + chunkSize, (uint32_t)posAfter); + + // Verify content + for (int i = 0; i < chunkSize; ++i) { + TEST_ASSERT_EQUAL_UINT8(writeBuffer[i], readBuffer[i]); + } + } + + f.close(); + } + + V.remove(path); +} + +void test_empty_file_operations() { + auto &V = gFS->vfs(); + const char *path = "/empty.txt"; + + { + // Create empty file + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.size()); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.position()); + f.close(); + } + + { + // Verify empty file exists and has zero size + TEST_ASSERT_TRUE(V.exists(path)); + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.size()); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.position()); + TEST_ASSERT_FALSE(f.available()); + f.close(); + } + + { + // Try to read from empty file + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + int b = f.read(); + TEST_ASSERT_EQUAL_INT(-1, b); // EOF + String s = f.readString(); + TEST_ASSERT_EQUAL_STRING("", s.c_str()); + f.close(); + } + + V.remove(path); +} + +void test_seek_edge_cases() { + auto &V = gFS->vfs(); + const char *path = "/seek_test.dat"; + const size_t fileSize = 1024; + + { + // Create file with known pattern + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + for (size_t i = 0; i < fileSize; ++i) { + f.write((uint8_t)(i & 0xFF)); + } + f.close(); + } + + { + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(fileSize, (uint32_t)f.size()); + + // Seek to beginning + TEST_ASSERT_TRUE(f.seek(0)); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.position()); + TEST_ASSERT_EQUAL_INT(0, f.read()); + + // Seek to middle + size_t mid = fileSize / 2; + TEST_ASSERT_TRUE(f.seek(mid)); + TEST_ASSERT_EQUAL_UINT32(mid, (uint32_t)f.position()); + TEST_ASSERT_EQUAL_INT(mid & 0xFF, f.read()); + + // Seek to end + TEST_ASSERT_TRUE(f.seek(fileSize)); + TEST_ASSERT_EQUAL_UINT32(fileSize, (uint32_t)f.position()); + TEST_ASSERT_FALSE(f.available()); + TEST_ASSERT_EQUAL_INT(-1, f.read()); // EOF + + // Seek back to beginning + TEST_ASSERT_TRUE(f.seek(0)); + TEST_ASSERT_EQUAL_UINT32(0, (uint32_t)f.position()); + f.close(); + } + + V.remove(path); +} + +void test_file_truncation_and_overwrite() { + auto &V = gFS->vfs(); + const char *path = "/trunc.txt"; + + { + // Write large file + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + for (int i = 0; i < 1000; ++i) { + f.print("data"); + } + f.close(); + + File check = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(check); + size_t largeSize = check.size(); + check.close(); + TEST_ASSERT_GREATER_THAN(1000, (int)largeSize); + } + + { + // Overwrite with smaller content (truncation) + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + f.print("small"); + f.close(); + + // Check size after closing + File check = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(check); + size_t smallSize = check.size(); + check.close(); + TEST_ASSERT_LESS_THAN(100, (int)smallSize); + } + + { + // Verify truncated content + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + String content = f.readString(); + TEST_ASSERT_EQUAL_STRING("small", content.c_str()); + f.close(); + } + + V.remove(path); +} + +void test_multiple_file_handles() { + auto &V = gFS->vfs(); + const char *path1 = "/multi1.txt"; + const char *path2 = "/multi2.txt"; + + { + // Open multiple files for writing + File f1 = V.open(path1, FILE_WRITE); + File f2 = V.open(path2, FILE_WRITE); + TEST_ASSERT_TRUE(f1); + TEST_ASSERT_TRUE(f2); + + f1.print("file1"); + f2.print("file2"); + + f1.close(); + f2.close(); + + // Verify sizes after closing (more reliable) + File check1 = V.open(path1, FILE_READ); + File check2 = V.open(path2, FILE_READ); + TEST_ASSERT_TRUE(check1); + TEST_ASSERT_TRUE(check2); + TEST_ASSERT_EQUAL_UINT32(5, (uint32_t)check1.size()); + TEST_ASSERT_EQUAL_UINT32(5, (uint32_t)check2.size()); + check1.close(); + check2.close(); + } + + { + // Open multiple files for reading + File f1 = V.open(path1, FILE_READ); + File f2 = V.open(path2, FILE_READ); + TEST_ASSERT_TRUE(f1); + TEST_ASSERT_TRUE(f2); + + String s1 = f1.readString(); + String s2 = f2.readString(); + + TEST_ASSERT_EQUAL_STRING("file1", s1.c_str()); + TEST_ASSERT_EQUAL_STRING("file2", s2.c_str()); + + f1.close(); + f2.close(); + } + + V.remove(path1); + V.remove(path2); +} + +void test_free_space_tracking() { + size_t initialUsed = gFS->usedBytes(); + size_t total = gFS->totalBytes(); + TEST_ASSERT_GREATER_THAN(0, (int)total); + + auto &V = gFS->vfs(); + const char *path = "/space_test.dat"; + const size_t testSize = 4096; + + { + // Write file and check space usage + size_t usedBefore = gFS->usedBytes(); + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + + for (size_t i = 0; i < testSize; ++i) { + f.write((uint8_t)(i & 0xFF)); + } + f.close(); + + size_t usedAfter = gFS->usedBytes(); + TEST_ASSERT_GREATER_OR_EQUAL_UINT32(usedBefore, (uint32_t)usedAfter); + // Note: usedBytes may not increase immediately due to caching/buffering + } + + { + // Remove file and verify space is freed + size_t usedBefore = gFS->usedBytes(); + TEST_ASSERT_TRUE(V.remove(path)); + size_t usedAfter = gFS->usedBytes(); + // Space should be freed (or at least not increase) + TEST_ASSERT_LESS_OR_EQUAL_UINT32(usedBefore, (uint32_t)usedAfter); + } + + // Final used should be close to initial (allowing for filesystem overhead) + size_t finalUsed = gFS->usedBytes(); + TEST_ASSERT_LESS_OR_EQUAL_UINT32(initialUsed + 10000, (uint32_t)finalUsed); // Allow some overhead +} + +void test_error_cases() { + if (strcmp(gFS->name(), "spiffs") == 0) { + TEST_MESSAGE("Skipping error case tests for SPIFFS due to different error handling"); + return; + } + + auto &V = gFS->vfs(); + + TEST_ASSERT_FALSE(V.open("/nonexistent.txt", FILE_READ)); + TEST_ASSERT_FALSE(V.remove("/nonexistent.txt")); + TEST_ASSERT_FALSE(V.rename("/nonexistent.txt", "/newname.txt")); + TEST_ASSERT_FALSE(V.rmdir("/nonexistent_dir")); +} + +void test_large_file_operations() { + auto &V = gFS->vfs(); + const char *path = "/large.dat"; + const size_t largeSize = 10 * 1024; // 10KB + uint8_t buffer[256]; + + // Initialize buffer + for (int i = 0; i < 256; ++i) { + buffer[i] = (uint8_t)i; + } + + { + // Write large file in chunks + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + + size_t totalWritten = 0; + for (size_t i = 0; i < largeSize; i += 256) { + size_t toWrite = (largeSize - i < 256) ? (largeSize - i) : 256; + size_t written = f.write(buffer, toWrite); + TEST_ASSERT_EQUAL_UINT32(toWrite, (uint32_t)written); + totalWritten += written; + + // Verify position grows correctly (more reliable than size during write) + TEST_ASSERT_EQUAL_UINT32(totalWritten, (uint32_t)f.position()); + } + + f.close(); + + // Verify final size + File check = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(check); + TEST_ASSERT_EQUAL_UINT32(largeSize, (uint32_t)check.size()); + check.close(); + } + + { + // Read back large file + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + TEST_ASSERT_EQUAL_UINT32(largeSize, (uint32_t)f.size()); + + size_t totalRead = 0; + uint8_t readBuffer[256]; + while (totalRead < largeSize) { + size_t toRead = (largeSize - totalRead < 256) ? (largeSize - totalRead) : 256; + size_t read = f.read(readBuffer, toRead); + TEST_ASSERT_GREATER_THAN(0, (int)read); + totalRead += read; + } + + TEST_ASSERT_EQUAL_UINT32(largeSize, (uint32_t)totalRead); + f.close(); + } + + V.remove(path); +} + +void test_write_read_patterns() { + auto &V = gFS->vfs(); + const char *path = "/pattern.dat"; + + { + // Sequential write pattern + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + + for (int i = 0; i < 100; ++i) { + size_t posBefore = f.position(); + f.write((uint8_t)i); + size_t posAfter = f.position(); + TEST_ASSERT_EQUAL_UINT32(posBefore + 1, (uint32_t)posAfter); + } + + f.close(); + + // Verify final size after closing + File check = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(check); + TEST_ASSERT_EQUAL_UINT32(100, (uint32_t)check.size()); + check.close(); + } + + { + // Random access read pattern + File f = V.open(path, FILE_READ); + TEST_ASSERT_TRUE(f); + + // Read from various positions + int positions[] = {0, 10, 50, 99, 25, 75}; + for (int i = 0; i < 6; ++i) { + int pos = positions[i]; + TEST_ASSERT_TRUE(f.seek(pos)); + int value = f.read(); + TEST_ASSERT_EQUAL_INT(pos, value); + } + + f.close(); + } + + V.remove(path); +} + +void test_directory_operations_edge_cases() { + auto &V = gFS->vfs(); + TEST_ASSERT_TRUE(V.mkdir("/test_dir")); + + if (strcmp(gFS->name(), "spiffs") != 0) { + // it should be fine to create again the same dir + TEST_ASSERT_TRUE(V.mkdir("/test_dir")); + + // creating nested dirs without parent should fail same as rmdir non-existent + TEST_ASSERT_FALSE(V.mkdir("/deep/nested/path")); + TEST_ASSERT_FALSE(V.rmdir("/nonexistent_dir")); + } + V.rmdir("/test_dir"); +} + +void test_max_open_files_limit() { + if (strcmp(gFS->name(), "littlefs") == 0) { + TEST_MESSAGE("Skipping: LittleFS does not have a max open files limit"); + return; + } + + auto &V = gFS->vfs(); + + // Create test files first + { + for (int i = 0; i < MAX_TEST_OPEN_FILES; ++i) { + char path[16]; + snprintf(path, sizeof(path), "/max%d.txt", i + 1); + File f = V.open(path, FILE_WRITE); + TEST_ASSERT_TRUE(f); + f.print("file" + String(i + 1)); + f.close(); + } + } + + // Open files up to the limit + File files[MAX_TEST_OPEN_FILES + 1]; + int openedCount = 0; + + // Open maxOpen files - all should succeed + for (int i = 0; i < MAX_TEST_OPEN_FILES; ++i) { + char path[16]; + snprintf(path, sizeof(path), "/max%d.txt", i + 1); + files[i] = V.open(path, FILE_READ); + if (files[i]) { + openedCount++; + } + } + + // Verify we opened exactly maxOpen files + TEST_ASSERT_EQUAL_INT(MAX_TEST_OPEN_FILES, openedCount); + + // Try to open one more file beyond the limit + File extraFile = V.open("/max1.txt", FILE_READ); + TEST_ASSERT_FALSE_MESSAGE(extraFile, "Opened file beyond maxOpen limit"); + + // Close one file + files[0].close(); + openedCount--; + + // Now we should be able to open a new file + File newFile = V.open("/max1.txt", FILE_READ); + TEST_ASSERT_TRUE(newFile); + newFile.close(); + + // Cleanup test files + for (int i = 0; i < MAX_TEST_OPEN_FILES; ++i) { + if (files[i]) { + files[i].close(); + } + + char path[16]; + snprintf(path, sizeof(path), "/max%d.txt", i + 1); + V.remove(path); + } +} + +// ---------------- Run the same test set over all FS ---------------- + +static void run_suite_for(IFileSystem &fs) { + gFS = &fs; + Serial.println(); + Serial.print("=== FS: "); + Serial.println(fs.name()); + + RUN_TEST(test_info_sanity); + RUN_TEST(test_basic_write_and_read); + RUN_TEST(test_append_behavior); + RUN_TEST(test_dir_ops_and_list); + RUN_TEST(test_rename_and_remove); + RUN_TEST(test_binary_write_and_seek); + RUN_TEST(test_binary_incremental_with_size_tracking); + RUN_TEST(test_empty_file_operations); + RUN_TEST(test_seek_edge_cases); + RUN_TEST(test_file_truncation_and_overwrite); + RUN_TEST(test_multiple_file_handles); + RUN_TEST(test_free_space_tracking); + RUN_TEST(test_error_cases); + RUN_TEST(test_large_file_operations); + RUN_TEST(test_write_read_patterns); + RUN_TEST(test_directory_operations_edge_cases); + RUN_TEST(test_max_open_files_limit); + gFS = nullptr; +} + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(10); + } + + UNITY_BEGIN(); + + run_suite_for(FS_SPIFFS); + run_suite_for(FS_FFAT); + run_suite_for(FS_LFS); + + UNITY_END(); +} + +void loop() {} diff --git a/tests/validation/fs/partitions.csv b/tests/validation/fs/partitions.csv new file mode 100644 index 00000000000..60c4e7163d7 --- /dev/null +++ b/tests/validation/fs/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs,data,nvs,0x9000,0x5000, +factory,app,factory,0x10000,0x180000, +fat,data,fat,0x190000,0x85000, +spiffs,data,spiffs,0x215000,0x43000, +littlefs,data,littlefs,0x258000,0x41000, +coredump,data,coredump,0x299000,0x1E000, diff --git a/tests/validation/fs/test_fs.py b/tests/validation/fs/test_fs.py new file mode 100644 index 00000000000..7c54f621598 --- /dev/null +++ b/tests/validation/fs/test_fs.py @@ -0,0 +1,5 @@ +from pytest_embedded import Dut + + +def test_fs(dut: Dut): + dut.expect_unity_test_output(timeout=300) diff --git a/tests/validation/gpio/ci.json b/tests/validation/gpio/ci.json deleted file mode 100644 index 7bc6a6ed163..00000000000 --- a/tests/validation/gpio/ci.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "platforms": { - "hardware": false, - "qemu": false - } -} diff --git a/tests/validation/gpio/ci.yml b/tests/validation/gpio/ci.yml new file mode 100644 index 00000000000..3f53d32a04e --- /dev/null +++ b/tests/validation/gpio/ci.yml @@ -0,0 +1,3 @@ +platforms: + hardware: false + qemu: false diff --git a/tests/validation/gpio/diagram.esp32.json b/tests/validation/gpio/diagram.esp32.json index 05b28156e37..23603bc60fd 100644 --- a/tests/validation/gpio/diagram.esp32.json +++ b/tests/validation/gpio/diagram.esp32.json @@ -13,16 +13,26 @@ { "type": "wokwi-pushbutton", "id": "btn1", - "top": -13, - "left": -19.2, - "attrs": { "color": "green" } + "top": 83, + "left": -38.4, + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -39.6, + "left": -41.4, + "rotate": 90, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v48", "h-38.4" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v57.8", "h-240", "v-76.8" ] ], + [ "esp32:GND.2", "led1:C", "black", [ "v0" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32c3.json b/tests/validation/gpio/diagram.esp32c3.json index c237e089ea2..e4c35c60667 100644 --- a/tests/validation/gpio/diagram.esp32c3.json +++ b/tests/validation/gpio/diagram.esp32c3.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 28, + "left": -286.6, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-28.8", "v144", "h-144", "v-95.7" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.4", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "v0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32c6.json b/tests/validation/gpio/diagram.esp32c6.json index 5020171f4e6..f019943df78 100644 --- a/tests/validation/gpio/diagram.esp32c6.json +++ b/tests/validation/gpio/diagram.esp32c6.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 56.8, + "left": -286.6, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v-96", "h-163.2", "v93.77" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32h2.json b/tests/validation/gpio/diagram.esp32h2.json index 48189dcea9f..e8c760d34db 100644 --- a/tests/validation/gpio/diagram.esp32h2.json +++ b/tests/validation/gpio/diagram.esp32h2.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -0.8, + "left": -267.4, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-19.2", "v-96", "h-163.2", "v93.77" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.2", "led1:C", "black", [ "h0" ] ], + [ "esp32:4", "led1:A", "green", [ "h-29.14", "v-26.57" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32p4.json b/tests/validation/gpio/diagram.esp32p4.json index ffb0cde2775..21bc4177896 100644 --- a/tests/validation/gpio/diagram.esp32p4.json +++ b/tests/validation/gpio/diagram.esp32p4.json @@ -15,14 +15,17 @@ "id": "btn1", "top": -128.2, "left": -19.2, - "attrs": { "color": "green", "bounce": "1" } - } + "attrs": { "color": "green", "bounce": "0" } + }, + { "type": "wokwi-led", "id": "led1", "top": -138, "left": -92.2, "attrs": { "color": "red" } } ], "connections": [ [ "esp32:38", "$serialMonitor:TX", "", [] ], [ "esp32:37", "$serialMonitor:RX", "", [] ], [ "btn1:2.r", "esp32:GND.3", "black", [ "h19.4", "v29" ] ], - [ "esp32:0", "btn1:1.l", "blue", [ "h-48", "v-67.2" ] ] + [ "esp32:0", "btn1:1.l", "blue", [ "h-48", "v-67.2" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "v0" ] ], + [ "esp32:4", "led1:A", "green", [ "v-19.2", "h-48" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32s2.json b/tests/validation/gpio/diagram.esp32s2.json index e3f850e193e..6607863962d 100644 --- a/tests/validation/gpio/diagram.esp32s2.json +++ b/tests/validation/gpio/diagram.esp32s2.json @@ -15,14 +15,24 @@ "id": "btn1", "top": -22.6, "left": -19.2, - "attrs": { "color": "green" } + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": -0.8, + "left": -277, + "rotate": 270, + "attrs": { "color": "red" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-28.8", "v-57.6", "h-144", "v42.71" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ] + [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v173", "h-269.2", "v-98.23" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h-67.47", "v-167.51" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/diagram.esp32s3.json b/tests/validation/gpio/diagram.esp32s3.json index ad9f9e0308a..11b14e86cf6 100644 --- a/tests/validation/gpio/diagram.esp32s3.json +++ b/tests/validation/gpio/diagram.esp32s3.json @@ -13,16 +13,26 @@ { "type": "wokwi-pushbutton", "id": "btn1", - "top": -22.6, - "left": -19.2, - "attrs": { "color": "green" } + "top": 83, + "left": 9.6, + "attrs": { "color": "green", "bounce": "0" } + }, + { + "type": "wokwi-led", + "id": "led1", + "top": 66.4, + "left": -257.8, + "rotate": 270, + "attrs": { "color": "red", "flip": "" } } ], "connections": [ [ "esp32:RX", "$serialMonitor:TX", "", [] ], [ "esp32:TX", "$serialMonitor:RX", "", [] ], [ "btn1:1.l", "esp32:0", "blue", [ "h-38.4", "v105.78" ] ], - [ "btn1:2.r", "esp32:GND.1", "black", [ "h19.4", "v221", "h-269.2", "v-57.42" ] ] + [ "btn1:2.r", "esp32:GND.3", "green", [ "h19.4", "v48.2", "h-144.4", "v0.18" ] ], + [ "esp32:4", "led1:A", "green", [ "h0" ] ], + [ "esp32:GND.1", "led1:C", "black", [ "h0" ] ] ], "dependencies": {} } diff --git a/tests/validation/gpio/gpio.ino b/tests/validation/gpio/gpio.ino index a5bec1cb5a3..b0c3f7ec0bd 100644 --- a/tests/validation/gpio/gpio.ino +++ b/tests/validation/gpio/gpio.ino @@ -1,31 +1,241 @@ +/** + * GPIO Validation Test + * There are multiple synchronization points in this test. + * They are required for proper timing between the running code and wokwi-pytest. + * Without them, the test sometimes fails due to timing issues. + */ + #include #include #define BTN 0 +#define LED 4 + +volatile int interruptCounter = 0; +volatile bool interruptFlag = false; +volatile unsigned long lastInterruptTime = 0; + +// Variables for interrupt with argument test +volatile int argInterruptCounter = 0; +volatile bool argInterruptFlag = false; +volatile int receivedArg = 0; -void test_button() { - Serial.println("Button test"); - static int count = 0; - static int lastState = HIGH; - while (count < 3) { - int state = digitalRead(BTN); - if (state != lastState) { - if (state == LOW) { - count++; - Serial.print("Button pressed "); - Serial.print(count); - Serial.println(" times"); - } - lastState = state; +void waitForSyncAck(const String &token = "OK") { + while (true) { + String response = Serial.readStringUntil('\n'); + response.trim(); + if (response.equalsIgnoreCase(token)) { + break; } delay(10); } } +void setUp(void) { + interruptCounter = 0; + interruptFlag = false; + lastInterruptTime = 0; + argInterruptCounter = 0; + argInterruptFlag = false; + receivedArg = 0; +} + +void tearDown(void) {} + +void IRAM_ATTR buttonISR() { + unsigned long currentTime = millis(); + if (currentTime - lastInterruptTime > 50) { + interruptCounter = interruptCounter + 1; + interruptFlag = true; + lastInterruptTime = currentTime; + } +} + +void IRAM_ATTR buttonISRWithArg(void *arg) { + unsigned long currentTime = millis(); + if (currentTime - lastInterruptTime > 50) { + argInterruptCounter = argInterruptCounter + 1; + argInterruptFlag = true; + receivedArg = *(int *)arg; + lastInterruptTime = currentTime; + } +} + +void test_read_basic(void) { + pinMode(BTN, INPUT_PULLUP); + TEST_ASSERT_EQUAL(HIGH, digitalRead(BTN)); + Serial.println("BTN read as HIGH after pinMode INPUT_PULLUP"); + + waitForSyncAck(); // sync ack R1 + TEST_ASSERT_EQUAL(LOW, digitalRead(BTN)); + Serial.println("BTN read as LOW"); + + waitForSyncAck(); // sync ack R2 + TEST_ASSERT_EQUAL(HIGH, digitalRead(BTN)); + Serial.println("BTN read as HIGH"); +} + +void test_write_basic(void) { + pinMode(LED, OUTPUT); + Serial.println("GPIO LED set to OUTPUT"); + waitForSyncAck(); // sync ack W1 + + digitalWrite(LED, HIGH); + Serial.println("LED set to HIGH"); + waitForSyncAck(); // sync ack W2 + + digitalWrite(LED, LOW); + Serial.println("LED set to LOW"); +} + +void test_interrupt_attach_detach(void) { + pinMode(BTN, INPUT_PULLUP); + pinMode(LED, OUTPUT); + digitalWrite(LED, LOW); + + interruptCounter = 0; + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, FALLING); + Serial.println("Interrupt attached - FALLING edge"); + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " interrupt triggered successfully"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Interrupt detached"); + + interruptCounter = 0; + interruptFlag = false; + + TEST_ASSERT_FALSE(interruptFlag); + TEST_ASSERT_EQUAL(0, interruptCounter); + Serial.println("No interrupt triggered after detach"); + waitForSyncAck(); +} + +void test_interrupt_falling(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, FALLING); + Serial.println("Testing FALLING edge interrupt"); + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " FALLING edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing FALLING edge END"); + waitForSyncAck(); +} + +void test_interrupt_rising(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, RISING); + Serial.println("Testing RISING edge interrupt"); + + interruptCounter = 0; + interruptFlag = false; + + for (int i = 1; i <= 3; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " RISING edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing RISING edge END"); + waitForSyncAck(); +} + +void test_interrupt_change(void) { + pinMode(BTN, INPUT_PULLUP); + + interruptCounter = 0; + interruptFlag = false; + + attachInterrupt(digitalPinToInterrupt(BTN), buttonISR, CHANGE); + Serial.println("Testing CHANGE edge interrupt"); + + for (int i = 1; i <= 6; i++) { + interruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(interruptFlag); + TEST_ASSERT_EQUAL(i, interruptCounter); + Serial.println(String(i) + " CHANGE edge interrupt worked"); + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing CHANGE edge END"); + waitForSyncAck(); +} + +void test_interrupt_with_arg(void) { + pinMode(BTN, INPUT_PULLUP); + + int argValue = 42; // Example argument to pass + interruptCounter = 0; + argInterruptFlag = false; + + attachInterruptArg(digitalPinToInterrupt(BTN), buttonISRWithArg, &argValue, FALLING); + Serial.println("Testing interrupt with argument"); + + for (int i = 1; i <= 3; i++) { + argInterruptFlag = false; + waitForSyncAck("OK:" + String(i)); + + TEST_ASSERT_TRUE(argInterruptFlag); + TEST_ASSERT_EQUAL(i, argInterruptCounter); + TEST_ASSERT_EQUAL(argValue, receivedArg); + Serial.println(String(i) + " interrupt with argument worked, received arg: " + String(receivedArg)); + ++argValue; + } + + detachInterrupt(digitalPinToInterrupt(BTN)); + Serial.println("Testing interrupt with argument END"); + waitForSyncAck(); +} + void setup() { Serial.begin(115200); - pinMode(BTN, INPUT_PULLUP); - test_button(); + while (!Serial) {} + + UNITY_BEGIN(); + + Serial.println("GPIO test START"); + RUN_TEST(test_read_basic); + RUN_TEST(test_write_basic); + + Serial.println("GPIO interrupt START"); + RUN_TEST(test_interrupt_attach_detach); + RUN_TEST(test_interrupt_falling); + RUN_TEST(test_interrupt_rising); + + RUN_TEST(test_interrupt_change); + RUN_TEST(test_interrupt_with_arg); + + UNITY_END(); + Serial.println("GPIO test END"); } void loop() {} diff --git a/tests/validation/gpio/scenario.yaml b/tests/validation/gpio/scenario.yaml deleted file mode 100644 index 957f58b2176..00000000000 --- a/tests/validation/gpio/scenario.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Pushbutton counter test -version: 1 -author: Jan Prochazka (jan.prochazka@espressif.com) - -steps: - - wait-serial: "Button test" - - # Need for 1s delay for scenario to run properly - - delay: 5000ms - - # Press once - - set-control: - part-id: btn1 - control: pressed - value: 1 - - delay: 2000ms - - set-control: - part-id: btn1 - control: pressed - value: 0 - - delay: 3000ms - - # Press 2nd time - - set-control: - part-id: btn1 - control: pressed - value: 1 - - delay: 2000ms - - set-control: - part-id: btn1 - control: pressed - value: 0 - - delay: 3000ms - - # Press for the 3rd time - - set-control: - part-id: btn1 - control: pressed - value: 1 - - wait-serial: "Button pressed 3 times" diff --git a/tests/validation/gpio/test_gpio.py b/tests/validation/gpio/test_gpio.py index 8aa3a42dcc6..30f32562c88 100644 --- a/tests/validation/gpio/test_gpio.py +++ b/tests/validation/gpio/test_gpio.py @@ -1,16 +1,114 @@ import logging +from pytest_embedded_wokwi import Wokwi +from pytest_embedded import Dut +from time import sleep -def test_gpio(dut): +def test_gpio(dut: Dut, wokwi: Wokwi): LOGGER = logging.getLogger(__name__) - dut.expect_exact("Button test") + def test_read_basic(): + dut.expect_exact("BTN read as HIGH after pinMode INPUT_PULLUP") + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write("OK\n") # Sync ack R1 + dut.expect_exact("BTN read as LOW") - LOGGER.info("Expecting button press 1") - dut.expect_exact("Button pressed 1 times") + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write("OK\n") # Sync ack R2 + dut.expect_exact("BTN read as HIGH") + LOGGER.info("GPIO read basic test passed.") - LOGGER.info("Expecting button press 2") - dut.expect_exact("Button pressed 2 times") + def test_write_basic(): + dut.expect_exact("GPIO LED set to OUTPUT") + assert wokwi.client.read_pin("led1", "A")["value"] == 0 # Anode pin + wokwi.client.serial_write("OK\n") # Sync ack W1 - LOGGER.info("Expecting button press 3") - dut.expect_exact("Button pressed 3 times") + dut.expect_exact("LED set to HIGH") + assert wokwi.client.read_pin("led1", "A")["value"] == 1 + wokwi.client.serial_write("OK\n") # Sync ack W2 + + dut.expect_exact("LED set to LOW") + assert wokwi.client.read_pin("led1", "A")["value"] == 0 + LOGGER.info("GPIO write basic test passed.") + + def test_interrupt_attach_detach(): + dut.expect_exact("Interrupt attached - FALLING edge") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} interrupt triggered successfully") + wokwi.client.set_control("btn1", "pressed", 0) + + dut.expect_exact("Interrupt detached") + wokwi.client.set_control("btn1", "pressed", 1) + sleep(0.1) + dut.expect_exact("No interrupt triggered after detach") + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt attach/detach test passed.") + + def test_interrupt_falling(): + dut.expect_exact("Testing FALLING edge interrupt") + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} FALLING edge interrupt worked") + wokwi.client.set_control("btn1", "pressed", 0) + + dut.expect_exact("Testing FALLING edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt falling test passed.") + + def test_interrupt_rising(): + dut.expect_exact("Testing RISING edge interrupt") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} RISING edge interrupt worked") + + dut.expect_exact("Testing RISING edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt rising test passed.") + + def test_interrupt_change(): + dut.expect_exact("Testing CHANGE edge interrupt") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i * 2 - 1}\n") + dut.expect_exact(f"{i * 2 - 1} CHANGE edge interrupt worked") + + wokwi.client.set_control("btn1", "pressed", 0) + wokwi.client.serial_write(f"OK:{i * 2}\n") + dut.expect_exact(f"{i * 2} CHANGE edge interrupt worked") + + dut.expect_exact("Testing CHANGE edge END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt change test passed.") + + def test_interrupt_with_arg(): + dut.expect_exact("Testing interrupt with argument") + + for i in range(1, 4): + wokwi.client.set_control("btn1", "pressed", 1) + wokwi.client.serial_write(f"OK:{i}\n") + dut.expect_exact(f"{i} interrupt with argument worked, received arg: {42 + i - 1}") + wokwi.client.set_control("btn1", "pressed", 0) + dut.expect_exact("Testing interrupt with argument END") + wokwi.client.serial_write("OK\n") + LOGGER.info("GPIO interrupt with argument test passed.") + + LOGGER.info("Waiting for GPIO test begin...") + dut.expect_exact("GPIO test START") + test_read_basic() + test_write_basic() + dut.expect_exact("GPIO interrupt START") + test_interrupt_attach_detach() + test_interrupt_falling() + test_interrupt_rising() + test_interrupt_change() + test_interrupt_with_arg() + LOGGER.info("GPIO test END") diff --git a/tests/validation/i2c_master/ci.json b/tests/validation/i2c_master/ci.json deleted file mode 100644 index 2b8792cd131..00000000000 --- a/tests/validation/i2c_master/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "platforms": { - "hardware": false, - "qemu": false - }, - "requires": [ - "CONFIG_SOC_I2C_SUPPORTED=y" - ] -} diff --git a/tests/validation/i2c_master/ci.yml b/tests/validation/i2c_master/ci.yml new file mode 100644 index 00000000000..fcf344e3e07 --- /dev/null +++ b/tests/validation/i2c_master/ci.yml @@ -0,0 +1,6 @@ +platforms: + hardware: false + qemu: false + +requires: + - CONFIG_SOC_I2C_SUPPORTED=y diff --git a/tests/validation/nvs/ci.json b/tests/validation/nvs/ci.json deleted file mode 100644 index 7f8ce83ec54..00000000000 --- a/tests/validation/nvs/ci.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "fqbn": { - "esp32": [ - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio" - ], - "esp32c3": [ - "espressif:esp32:esp32c3:PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32c3:PartitionScheme=huge_app,FlashMode=qio" - ], - "esp32c6": [ - "espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=dio,FlashFreq=40", - "espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=qio,FlashFreq=40" - ], - "esp32h2": [ - "espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=dio,FlashFreq=16", - "espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=qio,FlashFreq=16" - ], - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio" - ], - "esp32s3": [ - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio120", - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=dio" - ], - "esp32p4": [ - "espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=dio,FlashFreq=40", - "espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio,FlashFreq=40" - ] - }, - "platforms": { - "qemu": false - } -} diff --git a/tests/validation/nvs/ci.yml b/tests/validation/nvs/ci.yml new file mode 100644 index 00000000000..bebad7c1014 --- /dev/null +++ b/tests/validation/nvs/ci.yml @@ -0,0 +1,32 @@ +fqbn: + esp32: + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio + esp32c3: + - espressif:esp32:esp32c3:PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32c3:PartitionScheme=huge_app,FlashMode=qio + esp32c6: + - espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=dio,FlashFreq=40 + - espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32c6:PartitionScheme=huge_app,FlashMode=qio,FlashFreq=40 + esp32h2: + - espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=dio,FlashFreq=16 + - espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32h2:PartitionScheme=huge_app,FlashMode=qio,FlashFreq=16 + esp32s2: + - espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=qio + esp32s3: + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio120 + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=dio + esp32p4: + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=dio,FlashFreq=40 + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio,FlashFreq=40 + +platforms: + qemu: false diff --git a/tests/validation/periman/ci.json b/tests/validation/periman/ci.json deleted file mode 100644 index 22ff71c54ff..00000000000 --- a/tests/validation/periman/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - }, - "targets": { - "esp32p4": false - } -} diff --git a/tests/validation/periman/ci.yml b/tests/validation/periman/ci.yml new file mode 100644 index 00000000000..85d8f908f4e --- /dev/null +++ b/tests/validation/periman/ci.yml @@ -0,0 +1,6 @@ +platforms: + qemu: false + wokwi: false + +targets: + esp32p4: false diff --git a/tests/validation/periman/periman.ino b/tests/validation/periman/periman.ino index 8da59dd23b9..4e727bac7a3 100644 --- a/tests/validation/periman/periman.ino +++ b/tests/validation/periman/periman.ino @@ -158,7 +158,7 @@ void adc_continuous_test(void) { test_executed = true; uint8_t adc_pins[] = {ADC1_DEFAULT, ADC2_DEFAULT}; uint8_t adc_pins_count = 2; - adc_continuous_data_t *result = NULL; + adc_continuous_result_t *result = NULL; analogContinuousSetWidth(12); analogContinuousSetAtten(ADC_11db); diff --git a/tests/validation/psram/ci.json b/tests/validation/psram/ci.json deleted file mode 100644 index 999d3be953e..00000000000 --- a/tests/validation/psram/ci.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "platforms": { - "qemu": false - }, - "requires": [ - "CONFIG_SPIRAM=y" - ] -} diff --git a/tests/validation/psram/ci.yml b/tests/validation/psram/ci.yml new file mode 100644 index 00000000000..9324401b678 --- /dev/null +++ b/tests/validation/psram/ci.yml @@ -0,0 +1,16 @@ +soc_tags: + esp32: + - psram + esp32s2: + - psram + esp32s3: + - octal_psram + esp32c5: + - psram + # Runners for ESP32-P4 have PSRAM by default. There are no runners with psram tag. + +platforms: + qemu: false + +requires: + - CONFIG_SPIRAM=y diff --git a/tests/validation/touch/ci.json b/tests/validation/touch/ci.json deleted file mode 100644 index 855e9bd964d..00000000000 --- a/tests/validation/touch/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "platforms": { - "qemu": false, - "wokwi": false - }, - "requires": [ - "CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y" - ] -} diff --git a/tests/validation/touch/ci.yml b/tests/validation/touch/ci.yml new file mode 100644 index 00000000000..93fd0a8d591 --- /dev/null +++ b/tests/validation/touch/ci.yml @@ -0,0 +1,8 @@ +fqbn_append: DebugLevel=verbose + +platforms: + qemu: false + wokwi: false + +requires: + - CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y diff --git a/tests/validation/touch/touch.ino b/tests/validation/touch/touch.ino index 97aac8a65e6..21ed6119c02 100644 --- a/tests/validation/touch/touch.ino +++ b/tests/validation/touch/touch.ino @@ -1,35 +1,30 @@ #include #include "soc/soc_caps.h" -#include "driver/touch_pad.h" - -#if SOC_TOUCH_SENSOR_VERSION == 3 #include "hal/touch_sensor_ll.h" -#endif #if CONFIG_IDF_TARGET_ESP32 -#define TEST_TOUCH_CHANNEL (9) +#define TEST_TOUCH_CHANNEL (7) static touch_pad_t touch_list[TEST_TOUCH_CHANNEL] = { TOUCH_PAD_NUM0, //TOUCH_PAD_NUM1 is GPIO0, for download. - TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, TOUCH_PAD_NUM8, TOUCH_PAD_NUM9 + TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7 /*,TOUCH_PAD_NUM8, TOUCH_PAD_NUM9*/ }; -uint8_t TOUCH_GPIOS[] = {4, 2, 15, 13, 12, 14, 27, 33, 32}; +uint8_t TOUCH_GPIOS[] = {4, /* 0,*/ 2, 15, 13, 12, 14, 27 /*, 33, 32*/}; #define NO_TOUCH_GPIO 25 #elif (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) -#define TEST_TOUCH_CHANNEL (12) //14 +#define TEST_TOUCH_CHANNEL (8) //14 static touch_pad_t touch_list[TEST_TOUCH_CHANNEL] = { - TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, - TOUCH_PAD_NUM8, TOUCH_PAD_NUM9, TOUCH_PAD_NUM10, TOUCH_PAD_NUM11, TOUCH_PAD_NUM12 - //TOUCH_PAD_NUM13, //Wrong reading - //TOUCH_PAD_NUM14 + TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, + TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, TOUCH_PAD_NUM8 + /*TOUCH_PAD_NUM9, TOUCH_PAD_NUM10, TOUCH_PAD_NUM11, TOUCH_PAD_NUM12, TOUCH_PAD_NUM13, TOUCH_PAD_NUM14*/ }; -uint8_t TOUCH_GPIOS[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 /*,13,14*/}; +uint8_t TOUCH_GPIOS[] = {1, 2, 3, 4, 5, 6, 7, 8 /*, 9, 10, 11, 12, 13, 14*/}; #define NO_TOUCH_GPIO 17 @@ -47,21 +42,12 @@ uint8_t TOUCH_GPIOS[] = {2, 3, 4, 5, 6 /*, 7, 8, 9, 10, 11, 12 ,13, 14, 15*/}; #define NO_TOUCH_GPIO 17 #endif -#if CONFIG_IDF_TARGET_ESP32 -#define RELEASED_VALUE 75 //75+ read value to pass test -#define PRESSED_VALUE 20 //20- read value to pass test -#define INTERRUPT_THRESHOLD 40 -#elif CONFIG_IDF_TARGET_ESP32S2 -#define RELEASED_VALUE 10000 //10000- read value to pass test -#define PRESSED_VALUE 42000 //40000+ read value to pass test -#define INTERRUPT_THRESHOLD 30000 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define RELEASED_VALUE 25000 //25000- read value to pass test -#define PRESSED_VALUE 90000 //90000+ read value to pass test -#define INTERRUPT_THRESHOLD 80000 -#elif CONFIG_IDF_TARGET_ESP32P4 -#define PRESSED_VALUE_DIFFERENCE 200 //200+ read value difference against the unpressed value -#define INTERRUPT_THRESHOLD 0 // Use benchmarked threshold +#define INTERRUPT_THRESHOLD 0 // Use benchmarked threshold + +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 +#define PRESSED_VALUE_DIFFERENCE 200 //-200 for ESP32 and +200 for ESP32P4 read value difference against the unpressed value +#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2 +#define PRESSED_VALUE_DIFFERENCE 2000 //2000+ read value difference against the unpressed value #else #error Test not currently supported on this chip. Please adjust and try again! #endif @@ -82,7 +68,7 @@ void gotTouch2() { */ static void test_press_fake(touch_pad_t pad_num) { #if SOC_TOUCH_SENSOR_VERSION <= 2 - touch_pad_set_cnt_mode(pad_num, TOUCH_PAD_SLOPE_1, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_ll_set_charge_speed(pad_num, TOUCH_CHARGE_SPEED_4); #else touch_ll_set_internal_capacitor(0x7f); #endif @@ -93,7 +79,7 @@ static void test_press_fake(touch_pad_t pad_num) { */ static void test_release_fake(touch_pad_t pad_num) { #if SOC_TOUCH_SENSOR_VERSION <= 2 - touch_pad_set_cnt_mode(pad_num, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_ll_set_charge_speed(pad_num, TOUCH_CHARGE_SPEED_7); #else touch_ll_set_internal_capacitor(0); #endif @@ -113,31 +99,6 @@ void tearDown(void) { * Test Touch read on all available channels - compare values if reading is right */ void test_touch_read(void) { - -#if SOC_TOUCH_SENSOR_VERSION <= 2 - //TEST RELEASE STATE - for (int i = 0; i < sizeof(TOUCH_GPIOS); i++) { -#ifdef CONFIG_IDF_TARGET_ESP32 - TEST_ASSERT_GREATER_THAN(RELEASED_VALUE, touchRead(TOUCH_GPIOS[i])); -#else - TEST_ASSERT_LESS_THAN(RELEASED_VALUE, touchRead(TOUCH_GPIOS[i])); -#endif - } - - // TEST PRESS STATE - for (int j = 0; j < TEST_TOUCH_CHANNEL; j++) { - test_press_fake(touch_list[j]); - } - delay(100); - - for (int k = 0; k < sizeof(TOUCH_GPIOS); k++) { -#ifdef CONFIG_IDF_TARGET_ESP32 - TEST_ASSERT_LESS_THAN(PRESSED_VALUE, touchRead(TOUCH_GPIOS[k])); -#else - TEST_ASSERT_GREATER_THAN(PRESSED_VALUE, touchRead(TOUCH_GPIOS[k])); -#endif - } -#else //TOUCH V3 //TEST RELEASE STATE touch_value_t touch_unpressed[sizeof(TOUCH_GPIOS)]; for (int i = 0; i < sizeof(TOUCH_GPIOS); i++) { @@ -155,11 +116,18 @@ void test_touch_read(void) { touch_pressed[k] = touchRead(TOUCH_GPIOS[k]); } - // COMPARE PRESSED > UNPRESSED + // COMPARE PRESSED <-> UNPRESSED for (int l = 0; l < sizeof(TOUCH_GPIOS); l++) { - TEST_ASSERT_GREATER_THAN((touch_unpressed[l] + PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); + //log_i("Touch %d: %d -> %d", TOUCH_GPIOS[l], touch_unpressed[l], touch_pressed[l]); + Serial.printf("Touch %d: %lu -> %lu\n", TOUCH_GPIOS[l], touch_unpressed[l], touch_pressed[l]); } + for (int l = 0; l < sizeof(TOUCH_GPIOS); l++) { +#if CONFIG_IDF_TARGET_ESP32 + TEST_ASSERT_LESS_THAN((touch_unpressed[l] - PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); +#else + TEST_ASSERT_GREATER_THAN((touch_unpressed[l] + PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); #endif + } } void test_touch_interrtupt(void) { @@ -170,7 +138,7 @@ void test_touch_interrtupt(void) { test_press_fake(touch_list[0]); test_press_fake(touch_list[1]); - delay(300); + delay(100); touchDetachInterrupt(TOUCH_GPIOS[0]); touchDetachInterrupt(TOUCH_GPIOS[1]); @@ -190,6 +158,10 @@ void setup() { ; } +#if SOC_TOUCH_SENSOR_VERSION == 3 + touch_ll_enable_internal_capacitor(true); +#endif + UNITY_BEGIN(); RUN_TEST(test_touch_read); RUN_TEST(test_touch_interrtupt); diff --git a/tests/validation/uart/ci.json b/tests/validation/uart/ci.json deleted file mode 100644 index 54da33b6176..00000000000 --- a/tests/validation/uart/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "platforms": { - "qemu": false - } -} diff --git a/tests/validation/uart/ci.yml b/tests/validation/uart/ci.yml new file mode 100644 index 00000000000..948813f74eb --- /dev/null +++ b/tests/validation/uart/ci.yml @@ -0,0 +1,2 @@ +platforms: + qemu: false diff --git a/tests/validation/uart/uart.ino b/tests/validation/uart/uart.ino index 794fc9affc2..ee83c334e19 100644 --- a/tests/validation/uart/uart.ino +++ b/tests/validation/uart/uart.ino @@ -276,6 +276,10 @@ void enabled_uart_calls_test(void) { Serial1.setRxInvert(true); Serial1.setRxInvert(false); + log_d("Checking if Serial 1 TX can be inverted while running"); + Serial1.setTxInvert(true); + Serial1.setTxInvert(false); + Serial.println("Enabled UART calls test successful"); } @@ -351,6 +355,10 @@ void disabled_uart_calls_test(void) { Serial1.setRxInvert(true); Serial1.setRxInvert(false); + log_d("Checking if Serial 1 TX can be inverted when stopped"); + Serial1.setTxInvert(true); + Serial1.setTxInvert(false); + Serial.println("Disabled UART calls test successful"); } @@ -369,9 +377,11 @@ void change_pins_test(void) { UARTTestConfig &config = *uart_test_configs[0]; // pinMode will force enabling the internal pullup resistor (IDF 5.3.2 Change) pinMode(NEW_RX1, INPUT_PULLUP); - config.serial.setPins(NEW_RX1, NEW_TX1); + // Detaching both pins will result in stopping the UART driver + // Only detach one of the pins + config.serial.setPins(NEW_RX1, /*NEW_TX1*/ -1); TEST_ASSERT_EQUAL(NEW_RX1, uart_get_RxPin(config.uart_num)); - TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(config.uart_num)); + //TEST_ASSERT_EQUAL(NEW_TX1, uart_get_TxPin(config.uart_num)); uart_internal_loopback(config.uart_num, NEW_RX1); config.transmit_and_check_msg("using new pins"); @@ -379,9 +389,11 @@ void change_pins_test(void) { for (int i = 0; i < TEST_UART_NUM; i++) { UARTTestConfig &config = *uart_test_configs[i]; UARTTestConfig &next_uart = *uart_test_configs[(i + 1) % TEST_UART_NUM]; - config.serial.setPins(next_uart.default_rx_pin, next_uart.default_tx_pin); + // Detaching both pins will result in stopping the UART driver + // Only detach one of the pins + config.serial.setPins(next_uart.default_rx_pin, /*next_uart.default_tx_pin*/ -1); TEST_ASSERT_EQUAL(uart_get_RxPin(config.uart_num), next_uart.default_rx_pin); - TEST_ASSERT_EQUAL(uart_get_TxPin(config.uart_num), next_uart.default_tx_pin); + //TEST_ASSERT_EQUAL(uart_get_TxPin(config.uart_num), next_uart.default_tx_pin); uart_internal_loopback(config.uart_num, next_uart.default_rx_pin); config.transmit_and_check_msg("using new pins"); @@ -442,7 +454,9 @@ void periman_test(void) { for (auto *ref : uart_test_configs) { UARTTestConfig &config = *ref; - Wire.begin(config.default_rx_pin, config.default_tx_pin); + // Detaching both pins will result in stopping the UART driver + // Only detach one of the pins + Wire.begin(config.default_rx_pin, /*config.default_tx_pin*/ -1); config.recv_msg = ""; log_d("Trying to send message using UART%d with I2C enabled", config.uart_num); diff --git a/tests/validation/wifi/ci.json b/tests/validation/wifi/ci.json deleted file mode 100644 index 36e91b221cb..00000000000 --- a/tests/validation/wifi/ci.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "extra_tags": [ - "wifi" - ], - "fqbn": { - "esp32": [ - "espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=huge_app,FlashMode=dio" - ], - "esp32s2": [ - "espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio", - "espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=huge_app,FlashMode=dio" - ], - "esp32s3": [ - "espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio", - "espressif:esp32:esp32s3:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio" - ] - }, - "platforms": { - "hardware": false, - "qemu": false - }, - "requires": [ - "CONFIG_SOC_WIFI_SUPPORTED=y" - ] -} diff --git a/tests/validation/wifi/ci.yml b/tests/validation/wifi/ci.yml new file mode 100644 index 00000000000..56005ad43a0 --- /dev/null +++ b/tests/validation/wifi/ci.yml @@ -0,0 +1,21 @@ +tags: + - wifi_router + +fqbn: + esp32: + - espressif:esp32:esp32:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32:PSRAM=disabled,PartitionScheme=huge_app,FlashMode=dio + esp32s2: + - espressif:esp32:esp32s2:PSRAM=enabled,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32s2:PSRAM=disabled,PartitionScheme=huge_app,FlashMode=dio + esp32s3: + - espressif:esp32:esp32s3:PSRAM=opi,USBMode=default,PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32s3:PSRAM=disabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32s3:PSRAM=enabled,USBMode=default,PartitionScheme=huge_app,FlashMode=qio + +platforms: + hardware: false + qemu: false + +requires: + - CONFIG_SOC_WIFI_SUPPORTED=y diff --git a/tests/validation/wifi/test_wifi.py b/tests/validation/wifi/test_wifi.py index 5049aae7b85..39ec154ffdc 100644 --- a/tests/validation/wifi/test_wifi.py +++ b/tests/validation/wifi/test_wifi.py @@ -1,13 +1,36 @@ import logging +import pytest -def test_wifi(dut): +def test_wifi(dut, wifi_ssid, wifi_pass): LOGGER = logging.getLogger(__name__) + # Fail if no WiFi SSID is provided + if not wifi_ssid: + pytest.fail("WiFi SSID is required but not provided. Use --wifi-ssid argument.") + + # Wait for device to be ready and send WiFi credentials + LOGGER.info("Waiting for device to be ready...") + dut.expect_exact("Device ready for WiFi credentials") + + dut.expect_exact("Send SSID:") + LOGGER.info(f"Sending WiFi credentials: SSID={wifi_ssid}") + dut.write(f"{wifi_ssid}\n") + + dut.expect_exact("Send Password:") + LOGGER.info(f"Sending WiFi password: Password={wifi_pass}") + dut.write(f"{wifi_pass or ''}\n") + + # Verify credentials were received + dut.expect_exact(f"SSID: {wifi_ssid}") + dut.expect_exact(f"Password: {wifi_pass or ''}") + LOGGER.info("Starting WiFi Scan") dut.expect_exact("Scan start") dut.expect_exact("Scan done") - dut.expect_exact("Wokwi-GUEST") + + LOGGER.info(f"Looking for WiFi network: {wifi_ssid}") + dut.expect_exact(wifi_ssid) LOGGER.info("WiFi Scan done") LOGGER.info("Connecting to WiFi") diff --git a/tests/validation/wifi/wifi.ino b/tests/validation/wifi/wifi.ino index 696234505cc..4f33b1524a4 100644 --- a/tests/validation/wifi/wifi.ino +++ b/tests/validation/wifi/wifi.ino @@ -38,8 +38,8 @@ #include -const char *ssid = "Wokwi-GUEST"; -const char *password = ""; +String ssid = ""; +String password = ""; // WARNING: This function is called from a separate FreeRTOS task (thread)! void WiFiEvent(WiFiEvent_t event) { @@ -87,14 +87,73 @@ void WiFiGotIP(WiFiEvent_t event, WiFiEventInfo_t info) { Serial.println(IPAddress(info.got_ip.ip_info.ip.addr)); } +void readWiFiCredentials() { + Serial.println("Waiting for WiFi credentials..."); + + // Flush any existing data in serial buffer + while (Serial.available()) { + Serial.read(); + } + + Serial.println("Send SSID:"); + + // Wait for SSID + while (ssid.length() == 0) { + if (Serial.available()) { + ssid = Serial.readStringUntil('\n'); + ssid.trim(); + } + delay(100); + } + + // Flush any remaining data from SSID input + while (Serial.available()) { + Serial.read(); + } + + Serial.println("Send Password:"); + + // Wait for password (allow empty password) + bool password_received = false; + unsigned long timeout = millis() + 10000; // 10 second timeout + while (!password_received && millis() < timeout) { + if (Serial.available()) { + password = Serial.readStringUntil('\n'); + password.trim(); + password_received = true; // Accept even empty password + } + delay(100); + } + + // Flush any remaining data + while (Serial.available()) { + Serial.read(); + } + + Serial.print("SSID: "); + Serial.println(ssid); + Serial.print("Password: "); + Serial.println(password); +} + void setup() { Serial.begin(115200); + while (!Serial) { + delay(100); + } + // delete old config WiFi.disconnect(true); delay(1000); + // Wait for test to be ready + Serial.println("Device ready for WiFi credentials"); + + // Read WiFi credentials from serial + readWiFiCredentials(); + // Examples of different ways to register wifi events; // these handlers will be called from another thread. WiFi.onEvent(WiFiEvent); @@ -134,7 +193,7 @@ void setup() { // Delete the scan result to free memory for code below. WiFi.scanDelete(); - WiFi.begin(ssid, password); + WiFi.begin(ssid.c_str(), password.c_str()); Serial.println(); Serial.println(); diff --git a/tools/espota.exe b/tools/espota.exe index 8bee0c9036f..2748d038100 100644 Binary files a/tools/espota.exe and b/tools/espota.exe differ diff --git a/tools/espota.py b/tools/espota.py index fd95955a2f3..bc06cec87bf 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -6,6 +6,7 @@ # Modified since 2015-09-18 from Pascal Gollor (https://github.com/pgollor) # Modified since 2015-11-09 from Hristo Gochkov (https://github.com/me-no-dev) # Modified since 2016-01-03 from Matthew O'Gorman (https://githumb.com/mogorman) +# Modified since 2025-09-04 from Lucas Saavedra Vaz (https://github.com/lucasssvaz) # # This script will push an OTA update to the ESP # use it like: @@ -36,6 +37,19 @@ # - Incorporated exception handling to catch and handle potential errors. # - Made variable names more descriptive for better readability. # - Introduced constants for better code maintainability. +# +# Changes +# 2025-09-04: +# - Changed authentication to use PBKDF2-HMAC-SHA256 for challenge/response +# +# Changes +# 2025-09-18: +# - Fixed authentication when using old images with MD5 passwords +# +# Changes +# 2025-10-07: +# - Fixed authentication when images might use old MD5 hashes stored in the firmware + from __future__ import print_function import socket @@ -81,88 +95,287 @@ def update_progress(progress): sys.stderr.flush() -def serve(remote_addr, local_addr, remote_port, local_port, password, filename, command=FLASH): # noqa: C901 - # Create a TCP/IP socket - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - server_address = (local_addr, local_port) - logging.info("Starting on %s:%s", str(server_address[0]), str(server_address[1])) - try: - sock.bind(server_address) - sock.listen(1) - except Exception as e: - logging.error("Listen Failed: %s", str(e)) - return 1 - - content_size = os.path.getsize(filename) - file_md5 = hashlib.md5(open(filename, "rb").read()).hexdigest() - logging.info("Upload size: %d", content_size) - message = "%d %d %d %s\n" % (command, local_port, content_size, file_md5) - - # Wait for a connection +def send_invitation_and_get_auth_challenge(remote_addr, remote_port, message): + """ + Send invitation to ESP device and get authentication challenge. + Returns (success, auth_data, error_message) tuple. + """ + remote_address = (remote_addr, int(remote_port)) inv_tries = 0 data = "" + msg = "Sending invitation to %s " % remote_addr sys.stderr.write(msg) sys.stderr.flush() + while inv_tries < 10: inv_tries += 1 sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - remote_address = (remote_addr, int(remote_port)) try: sent = sock2.sendto(message.encode(), remote_address) # noqa: F841 except: # noqa: E722 sys.stderr.write("failed\n") sys.stderr.flush() sock2.close() - logging.error("Host %s Not Found", remote_addr) - return 1 + return False, None, "Host %s Not Found" % remote_addr + sock2.settimeout(TIMEOUT) try: - data = sock2.recv(37).decode() + # Try to read up to 69 bytes for new protocol (SHA256) + # If device sends less (37 bytes), it's using old MD5 protocol + data = sock2.recv(69).decode() + sock2.close() break except: # noqa: E722 sys.stderr.write(".") sys.stderr.flush() sock2.close() + sys.stderr.write("\n") sys.stderr.flush() + if inv_tries == 10: - logging.error("No response from the ESP") + return False, None, "No response from the ESP" + + return True, data, None + + +def authenticate( + remote_addr, remote_port, password, use_md5_password, use_old_protocol, filename, content_size, file_md5, nonce +): + """ + Perform authentication with the ESP device. + + Args: + use_md5_password: If True, hash password with MD5 instead of SHA256 + use_old_protocol: If True, use old MD5 challenge/response protocol (pre-3.3.1) + + Returns (success, error_message) tuple. + """ + cnonce_text = "%s%u%s%s" % (filename, content_size, file_md5, remote_addr) + remote_address = (remote_addr, int(remote_port)) + + if use_old_protocol: + # Generate client nonce (cnonce) + cnonce = hashlib.md5(cnonce_text.encode()).hexdigest() + + # Old MD5 challenge/response protocol (pre-3.3.1) + # 1. Hash the password with MD5 + password_hash = hashlib.md5(password.encode()).hexdigest() + + # 2. Create challenge response + challenge = "%s:%s:%s" % (password_hash, nonce, cnonce) + response = hashlib.md5(challenge.encode()).hexdigest() + expected_response_length = 32 + else: + # Generate client nonce (cnonce) using SHA256 for new protocol + cnonce = hashlib.sha256(cnonce_text.encode()).hexdigest() + + # New PBKDF2-HMAC-SHA256 challenge/response protocol (3.3.1+) + # The password can be hashed with either MD5 or SHA256 + if use_md5_password: + # Use MD5 for password hash (for devices that stored MD5 hashes) + logging.warning( + "Using insecure MD5 hash for password due to legacy device support. " + "Please upgrade devices to ESP32 Arduino Core 3.3.1+ for improved security." + ) + password_hash = hashlib.md5(password.encode()).hexdigest() + else: + # Use SHA256 for password hash (recommended) + password_hash = hashlib.sha256(password.encode()).hexdigest() + + # 2. Derive key using PBKDF2-HMAC-SHA256 with the password hash + salt = nonce + ":" + cnonce + derived_key = hashlib.pbkdf2_hmac("sha256", password_hash.encode(), salt.encode(), 10000) + derived_key_hex = derived_key.hex() + + # 3. Create challenge response + challenge = derived_key_hex + ":" + nonce + ":" + cnonce + response = hashlib.sha256(challenge.encode()).hexdigest() + expected_response_length = 64 + + # Send authentication response + sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + message = "%d %s %s\n" % (AUTH, cnonce, response) + sock2.sendto(message.encode(), remote_address) + sock2.settimeout(10) + try: + data = sock2.recv(expected_response_length).decode() + except: # noqa: E722 + sock2.close() + return False, "No Answer to our Authentication" + + if data != "OK": + sock2.close() + return False, data + + sock2.close() + return True, None + except Exception as e: + sock2.close() + return False, str(e) + + +def serve( # noqa: C901 + remote_addr, local_addr, remote_port, local_port, password, md5_target, filename, command=FLASH +): + # Create a TCP/IP socket + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_address = (local_addr, local_port) + logging.info("Starting on %s:%s", str(server_address[0]), str(server_address[1])) + try: + sock.bind(server_address) + sock.listen(1) + except Exception as e: + logging.error("Listen Failed: %s", str(e)) + return 1 + + content_size = os.path.getsize(filename) + with open(filename, "rb") as f: + file_md5 = hashlib.md5(f.read()).hexdigest() + logging.info("Upload size: %d", content_size) + message = "%d %d %d %s\n" % (command, local_port, content_size, file_md5) + + # Send invitation and get authentication challenge + success, data, error = send_invitation_and_get_auth_challenge(remote_addr, remote_port, message) + if not success: + logging.error(error) return 1 + if data != "OK": if data.startswith("AUTH"): nonce = data.split()[1] - cnonce_text = "%s%u%s%s" % (filename, content_size, file_md5, remote_addr) - cnonce = hashlib.md5(cnonce_text.encode()).hexdigest() - passmd5 = hashlib.md5(password.encode()).hexdigest() - result_text = "%s:%s:%s" % (passmd5, nonce, cnonce) - result = hashlib.md5(result_text.encode()).hexdigest() - sys.stderr.write("Authenticating...") - sys.stderr.flush() - message = "%d %s %s\n" % (AUTH, cnonce, result) - sock2.sendto(message.encode(), remote_address) - sock2.settimeout(10) - try: - data = sock2.recv(32).decode() - except: # noqa: E722 - sys.stderr.write("FAIL\n") - logging.error("No Answer to our Authentication") - sock2.close() - return 1 - if data != "OK": - sys.stderr.write("FAIL\n") - logging.error("%s", data) - sock2.close() - sys.exit(1) + nonce_length = len(nonce) + + # Detect protocol version based on nonce length: + # - 32 chars = Old MD5 protocol (pre-3.3.1) + # - 64 chars = New SHA256 protocol (3.3.1+) + + if nonce_length == 32: + # Scenario 1: Old device (pre-3.3.1) using MD5 protocol + logging.info("Detected old MD5 protocol (pre-3.3.1)") + sys.stderr.write("Authenticating (MD5 protocol)...") + sys.stderr.flush() + auth_success, auth_error = authenticate( + remote_addr, + remote_port, + password, + use_md5_password=True, + use_old_protocol=True, + filename=filename, + content_size=content_size, + file_md5=file_md5, + nonce=nonce, + ) + + if not auth_success: + sys.stderr.write("FAIL\n") + logging.error("Authentication Failed: %s", auth_error) + return 1 + + sys.stderr.write("OK\n") + logging.warning("====================================================================") + logging.warning("WARNING: Device is using old MD5 authentication protocol (pre-3.3.1)") + logging.warning("Please update to ESP32 Arduino Core 3.3.1+ for improved security.") + logging.warning("======================================================================") + + elif nonce_length == 64: + # New protocol (3.3.1+) - try SHA256 password first, then MD5 if it fails + + # Scenario 2: Try SHA256 password hash first (recommended for new devices) + if md5_target: + # User explicitly requested MD5 password hash + logging.info("Using MD5 password hash as requested") + sys.stderr.write("Authenticating (SHA256 protocol with MD5 password)...") + sys.stderr.flush() + auth_success, auth_error = authenticate( + remote_addr, + remote_port, + password, + use_md5_password=True, + use_old_protocol=False, + filename=filename, + content_size=content_size, + file_md5=file_md5, + nonce=nonce, + ) + else: + # Try SHA256 password hash first + sys.stderr.write("Authenticating...") + sys.stderr.flush() + auth_success, auth_error = authenticate( + remote_addr, + remote_port, + password, + use_md5_password=False, + use_old_protocol=False, + filename=filename, + content_size=content_size, + file_md5=file_md5, + nonce=nonce, + ) + + # Scenario 3: If SHA256 fails, try MD5 password hash (for devices with stored MD5 passwords) + if not auth_success: + logging.info("SHA256 password failed, trying MD5 password hash") + sys.stderr.write("Retrying with MD5 password...") + sys.stderr.flush() + + # Device is back in OTA_IDLE after auth failure, need to send new invitation + success, data, error = send_invitation_and_get_auth_challenge(remote_addr, remote_port, message) + if not success: + sys.stderr.write("FAIL\n") + logging.error("Failed to get new challenge for MD5 retry: %s", error) + return 1 + + if not data.startswith("AUTH"): + sys.stderr.write("FAIL\n") + logging.error("Expected AUTH challenge for MD5 retry, got: %s", data) + return 1 + + # Get new nonce for second attempt + nonce = data.split()[1] + + auth_success, auth_error = authenticate( + remote_addr, + remote_port, + password, + use_md5_password=True, + use_old_protocol=False, + filename=filename, + content_size=content_size, + file_md5=file_md5, + nonce=nonce, + ) + + if auth_success: + logging.warning("====================================================================") + logging.warning("WARNING: Device authenticated with MD5 password hash (deprecated)") + logging.warning("MD5 is cryptographically broken and should not be used.") + logging.warning( + "Please update your sketch to use either setPassword() or setPasswordHash()" + ) + logging.warning( + "with SHA256, then upload again to migrate to the new secure SHA256 protocol." + ) + logging.warning("======================================================================") + + if not auth_success: + sys.stderr.write("FAIL\n") + logging.error("Authentication Failed: %s", auth_error) + return 1 + + sys.stderr.write("OK\n") + else: + logging.error("Invalid nonce length: %d (expected 32 or 64)", nonce_length) return 1 - sys.stderr.write("OK\n") else: logging.error("Bad Answer: %s", data) - sock2.close() return 1 - sock2.close() logging.info("Waiting for device...") + try: sock.settimeout(10) connection, client_address = sock.accept() @@ -172,6 +385,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, logging.error("No response from device") sock.close() return 1 + try: with open(filename, "rb") as f: if PROGRESS: @@ -190,7 +404,9 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, try: connection.sendall(chunk) res = connection.recv(10) - last_response_contained_ok = "OK" in res.decode() + response_text = res.decode().strip() + last_response_contained_ok = "OK" in response_text + logging.debug("Chunk response: '%s'", response_text) except Exception as e: sys.stderr.write("\n") logging.error("Error Uploading: %s", str(e)) @@ -205,27 +421,45 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, sys.stderr.write("\n") logging.info("Waiting for result...") count = 0 - while count < 5: + received_any_response = False + while count < 10: # Increased from 5 to 10 attempts count += 1 - connection.settimeout(60) + connection.settimeout(30) # Reduced from 60s to 30s per attempt try: - data = connection.recv(32).decode() - logging.info("Result: %s", data) + data = connection.recv(32).decode().strip() + received_any_response = True + logging.info("Result attempt %d: '%s'", count, data) if "OK" in data: logging.info("Success") connection.close() return 0 + elif data: # Got some response but not OK + logging.warning("Unexpected response from device: '%s'", data) + except socket.timeout: + logging.debug("Timeout waiting for result (attempt %d/10)", count) + continue except Exception as e: - logging.error("Error receiving result: %s", str(e)) - connection.close() - return 1 - - logging.error("Error response from device") - connection.close() - return 1 - + logging.debug("Error receiving result (attempt %d/10): %s", count, str(e)) + # Don't return error here, continue trying + continue + + # After all attempts, provide detailed error information + if received_any_response: + logging.warning( + "Upload completed but device sent unexpected response(s). This may still be successful." + ) + logging.warning("Device might be rebooting to apply firmware - this is normal.") + connection.close() + return 0 # Consider it successful if we got any response and upload completed + else: + logging.error("No response from device after upload completion") + logging.error("This could indicate device reboot (normal) or network issues") + connection.close() + return 1 + except Exception as e: # noqa: E722 + logging.error("Error: %s", str(e)) finally: connection.close() @@ -251,6 +485,17 @@ def parse_args(unparsed_args): # authentication parser.add_argument("-a", "--auth", dest="auth", help="Set authentication password.", action="store", default="") + parser.add_argument( + "-m", + "--md5-target", + dest="md5_target", + help=( + "Use MD5 for password hashing (for devices with stored MD5 passwords). " + "By default, SHA256 is tried first, then MD5 as fallback." + ), + action="store_true", + default=False, + ) # image parser.add_argument("-f", "--file", dest="image", help="Image file.", metavar="FILE", default=None) @@ -317,7 +562,14 @@ def main(args): command = SPIFFS return serve( - options.esp_ip, options.host_ip, options.esp_port, options.host_port, options.auth, options.image, command + options.esp_ip, + options.host_ip, + options.esp_port, + options.host_port, + options.auth, + options.md5_target, + options.image, + command, ) diff --git a/tools/get.exe b/tools/get.exe index b56f2b98384..bc9abbad4be 100644 Binary files a/tools/get.exe and b/tools/get.exe differ diff --git a/tools/get.py b/tools/get.py index c791020b7e9..808d995d6b4 100755 --- a/tools/get.py +++ b/tools/get.py @@ -50,6 +50,49 @@ dist_dir = current_dir + "/dist/" +def is_safe_archive_path(path): + # Check for absolute paths (both Unix and Windows style) + if path.startswith("/") or (len(path) > 1 and path[1] == ":" and path[2] in "\\/"): + raise ValueError(f"Absolute path not allowed: {path}") + + # Normalize the path to handle any path separators + normalized_path = os.path.normpath(path) + + # Check for directory traversal attempts using normalized path + if ".." in normalized_path.split(os.sep): + raise ValueError(f"Directory traversal not allowed: {path}") + + # Additional check for paths that would escape the target directory + if normalized_path.startswith(".."): + raise ValueError(f"Path would escape target directory: {path}") + + # Check for any remaining directory traversal patterns in the original path + # This catches cases that might not be normalized properly + path_parts = path.replace("\\", "/").split("/") + if ".." in path_parts: + raise ValueError(f"Directory traversal not allowed: {path}") + + return True + + +def safe_tar_extract(tar_file, destination): + # Validate all paths before extraction + for member in tar_file.getmembers(): + is_safe_archive_path(member.name) + + # If all paths are safe, proceed with extraction + tar_file.extractall(destination, filter="tar") + + +def safe_zip_extract(zip_file, destination): + # Validate all paths before extraction + for name in zip_file.namelist(): + is_safe_archive_path(name) + + # If all paths are safe, proceed with extraction + zip_file.extractall(destination) + + def sha256sum(filename, blocksize=65536): hash = hashlib.sha256() with open(filename, "rb") as f: @@ -212,6 +255,10 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901 print("File corrupted or incomplete!") cfile = None file_is_corrupted = True + except ValueError as e: + print(f"Security validation failed: {e}") + cfile = None + file_is_corrupted = True if file_is_corrupted: corrupted_filename = filename + ".corrupted" @@ -243,15 +290,15 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901 if filename.endswith("tar.gz"): if not cfile: cfile = tarfile.open(filename, "r:gz") - cfile.extractall(destination, filter="tar") + safe_tar_extract(cfile, destination) elif filename.endswith("tar.xz"): if not cfile: cfile = tarfile.open(filename, "r:xz") - cfile.extractall(destination, filter="tar") + safe_tar_extract(cfile, destination) elif filename.endswith("zip"): if not cfile: cfile = zipfile.ZipFile(filename) - cfile.extractall(destination) + safe_zip_extract(cfile, destination) else: raise NotImplementedError("Unsupported archive type") @@ -348,9 +395,8 @@ def get_tool(tool, force_download, force_extract): urlretrieve(url, local_path, report_progress, context=ctx) elif "Windows" in sys_name: r = requests.get(url) - f = open(local_path, "wb") - f.write(r.content) - f.close() + with open(local_path, "wb") as f: + f.write(r.content) else: is_ci = os.environ.get("GITHUB_WORKSPACE") if is_ci: @@ -374,7 +420,8 @@ def get_tool(tool, force_download, force_extract): def load_tools_list(filename, platform): - tools_info = json.load(open(filename))["packages"][0]["tools"] + with open(filename, "r") as f: + tools_info = json.load(f)["packages"][0]["tools"] tools_to_download = [] for t in tools_info: if platform == "x86_64-mingw32": diff --git a/tools/pioarduino-build.py b/tools/pioarduino-build.py index 2bacd9fcaef..ac0a743610e 100644 --- a/tools/pioarduino-build.py +++ b/tools/pioarduino-build.py @@ -32,6 +32,8 @@ platform = env.PioPlatform() board_config = env.BoardConfig() build_mcu = board_config.get("build.mcu", "").lower() +chip_variant = board_config.get("build.chip_variant", "").lower() +chip_variant = chip_variant if chip_variant else build_mcu partitions_name = board_config.get("build.partitions", board_config.get("build.arduino.partitions", "")) FRAMEWORK_DIR = platform.get_package_dir("framework-arduinoespressif32") @@ -80,7 +82,7 @@ def get_bootloader_image(variants_dir): else generate_bootloader_image( join( FRAMEWORK_LIBS_DIR, - build_mcu, + chip_variant, "bin", "bootloader_${__get_board_boot_mode(__env__)}_${__get_board_f_boot(__env__)}.elf", ) @@ -159,7 +161,7 @@ def add_tinyuf2_extra_image(): SConscript( join( FRAMEWORK_LIBS_DIR, - build_mcu, + chip_variant, "pioarduino-build.py", ) ) diff --git a/variants/Bee_Data_Logger/pins_arduino.h b/variants/Bee_Data_Logger/pins_arduino.h index 1114ff0bdd3..2fa1026589f 100644 --- a/variants/Bee_Data_Logger/pins_arduino.h +++ b/variants/Bee_Data_Logger/pins_arduino.h @@ -60,6 +60,7 @@ static const uint8_t T14 = 14; static const uint8_t BOOT_BTN = 0; static const uint8_t VBAT_VOLTAGE = 1; +#define BAT_VOLT_PIN VBAT_VOLTAGE static const uint8_t VBUS_SENSE = 2; static const uint8_t LDO2 = 34; static const uint8_t RGB_DATA = 40; diff --git a/variants/Bee_Motion_S3/pins_arduino.h b/variants/Bee_Motion_S3/pins_arduino.h index 45f73b56ba0..38a5a9a0660 100644 --- a/variants/Bee_Motion_S3/pins_arduino.h +++ b/variants/Bee_Motion_S3/pins_arduino.h @@ -66,6 +66,7 @@ static const uint8_t T14 = 14; static const uint8_t BOOT_BTN = 0; static const uint8_t VBAT_VOLTAGE = 1; +#define BAT_VOLT_PIN VBAT_VOLTAGE static const uint8_t VBUS_SENSE = 2; static const uint8_t PIR = 4; static const uint8_t LIGHT = 3; diff --git a/variants/Bee_S3/pins_arduino.h b/variants/Bee_S3/pins_arduino.h index 9e76fff803e..906a8c6ec64 100644 --- a/variants/Bee_S3/pins_arduino.h +++ b/variants/Bee_S3/pins_arduino.h @@ -58,6 +58,7 @@ static const uint8_t T9 = 9; static const uint8_t T10 = 10; static const uint8_t VBAT_VOLTAGE = 1; +#define BAT_VOLT_PIN VBAT_VOLTAGE static const uint8_t RGB_DATA = 48; static const uint8_t RGB_PWR = 34; diff --git a/variants/Pcbcupid_GLYPH_C3/pins_arduino.h b/variants/Pcbcupid_GLYPH_C3/pins_arduino.h index 653c2c48828..5475de822ff 100644 --- a/variants/Pcbcupid_GLYPH_C3/pins_arduino.h +++ b/variants/Pcbcupid_GLYPH_C3/pins_arduino.h @@ -10,7 +10,8 @@ static const uint8_t LED_BUILTIN = 1; //MSR Used in on-board battery measurement static const uint8_t BAT_MEASURE = 0; -#define MSR BAT_MEASURE +#define BAT_VOLT_PIN BAT_MEASURE +#define MSR BAT_MEASURE static const uint8_t TX = 21; static const uint8_t RX = 20; diff --git a/variants/Pcbcupid_GLYPH_C6/pins_arduino.h b/variants/Pcbcupid_GLYPH_C6/pins_arduino.h index f06fb151244..223afbc6194 100644 --- a/variants/Pcbcupid_GLYPH_C6/pins_arduino.h +++ b/variants/Pcbcupid_GLYPH_C6/pins_arduino.h @@ -10,7 +10,8 @@ static const uint8_t LED_BUILTIN = 14; //MSR Used in on-board battery measurement static const uint8_t BAT_MEASURE = 0; -#define MSR BAT_MEASURE +#define BAT_VOLT_PIN BAT_MEASURE +#define MSR BAT_MEASURE static const uint8_t TX = 16; static const uint8_t RX = 17; diff --git a/variants/Pcbcupid_GLYPH_H2/pins_arduino.h b/variants/Pcbcupid_GLYPH_H2/pins_arduino.h index 20a385a9817..edc108a2a0d 100644 --- a/variants/Pcbcupid_GLYPH_H2/pins_arduino.h +++ b/variants/Pcbcupid_GLYPH_H2/pins_arduino.h @@ -10,7 +10,8 @@ static const uint8_t LED_BUILTIN = 0; //MSR Used in on-board battery measurement static const uint8_t BAT_MEASURE = 1; -#define MSR BAT_MEASURE +#define BAT_VOLT_PIN BAT_MEASURE +#define MSR BAT_MEASURE static const uint8_t TX = 24; static const uint8_t RX = 23; diff --git a/variants/XIAO_ESP32C5/pins_arduino.h b/variants/XIAO_ESP32C5/pins_arduino.h new file mode 100644 index 00000000000..a9559940a68 --- /dev/null +++ b/variants/XIAO_ESP32C5/pins_arduino.h @@ -0,0 +1,45 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0067 + +static const uint8_t LED_BUILTIN = 27; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 11; +static const uint8_t RX = 12; + +static const uint8_t SDA = 23; +static const uint8_t SCL = 24; + +static const uint8_t SS = 7; +static const uint8_t MOSI = 10; +static const uint8_t MISO = 9; +static const uint8_t SCK = 8; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; + +static const uint8_t D0 = 1; +static const uint8_t D1 = 0; +static const uint8_t D2 = 25; +static const uint8_t D3 = 7; +static const uint8_t D4 = 23; +static const uint8_t D5 = 24; +static const uint8_t D6 = 11; +static const uint8_t D7 = 12; +static const uint8_t D8 = 8; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; + +static const uint8_t BAT_VOLT_PIN = 6; +static const uint8_t BAT_VOLT_PIN_EN = 26; + +#endif /* Pins_Arduino_h */ diff --git a/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin b/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin index b11e5b236f0..465e294717f 100644 Binary files a/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin and b/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3/tinyuf2.bin b/variants/XIAO_ESP32S3/tinyuf2.bin index 86d981f8019..95d1c4cb53b 100644 Binary files a/variants/XIAO_ESP32S3/tinyuf2.bin and b/variants/XIAO_ESP32S3/tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin index b11e5b236f0..465e294717f 100644 Binary files a/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin and b/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3_Plus/pins_arduino.h b/variants/XIAO_ESP32S3_Plus/pins_arduino.h index de1c6093d44..ba7d1a5069d 100644 --- a/variants/XIAO_ESP32S3_Plus/pins_arduino.h +++ b/variants/XIAO_ESP32S3_Plus/pins_arduino.h @@ -55,6 +55,7 @@ static const uint8_t A8 = 7; static const uint8_t A9 = 8; static const uint8_t A10 = 9; static const uint8_t ADC_BAT = 10; +#define BAT_VOLT_PIN ADC_BAT static const uint8_t D0 = 1; static const uint8_t D1 = 2; diff --git a/variants/XIAO_ESP32S3_Plus/tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/tinyuf2.bin index 86d981f8019..95d1c4cb53b 100644 Binary files a/variants/XIAO_ESP32S3_Plus/tinyuf2.bin and b/variants/XIAO_ESP32S3_Plus/tinyuf2.bin differ diff --git a/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin index ef4e3bf6fd1..65cadafb884 100644 Binary files a/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_camera_esp32s3/pins_arduino.h b/variants/adafruit_camera_esp32s3/pins_arduino.h index 447204f7345..16dd31d43f5 100644 --- a/variants/adafruit_camera_esp32s3/pins_arduino.h +++ b/variants/adafruit_camera_esp32s3/pins_arduino.h @@ -43,6 +43,7 @@ static const uint8_t MISO = 37; static const uint8_t A0 = 17; static const uint8_t A1 = 18; static const uint8_t BATT_MONITOR = 4; +#define BAT_VOLT_PIN BATT_MONITOR static const uint8_t SHUTTER_BUTTON = 0; static const uint8_t TX = 43; diff --git a/variants/adafruit_camera_esp32s3/tinyuf2.bin b/variants/adafruit_camera_esp32s3/tinyuf2.bin index 5eb3429b516..b1b4931d4b8 100644 Binary files a/variants/adafruit_camera_esp32s3/tinyuf2.bin and b/variants/adafruit_camera_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32_v2/pins_arduino.h b/variants/adafruit_feather_esp32_v2/pins_arduino.h index f4af72aa98b..1b95efdb2f6 100644 --- a/variants/adafruit_feather_esp32_v2/pins_arduino.h +++ b/variants/adafruit_feather_esp32_v2/pins_arduino.h @@ -35,6 +35,7 @@ static const uint8_t A13 = 35; // vbat measure #define BATT_MONITOR 35 +#define BAT_VOLT_PIN BATT_MONITOR // internal switch #define BUTTON 38 diff --git a/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin index 0c49080d571..c87c70a9b32 100644 Binary files a/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2/tinyuf2.bin b/variants/adafruit_feather_esp32s2/tinyuf2.bin index 40a8f04f924..f74a61266f5 100644 Binary files a/variants/adafruit_feather_esp32s2/tinyuf2.bin and b/variants/adafruit_feather_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin index e4d6a498eb1..bb4b220b64b 100644 Binary files a/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin b/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin index ff6eb115dba..249593bc488 100644 Binary files a/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin and b/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin index aa7cb519661..3d1c246e9a4 100644 Binary files a/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin b/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin index b8334f98220..839edd1c351 100644 Binary files a/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin and b/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin index 8a04549dad5..f7312390e75 100644 Binary files a/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3/tinyuf2.bin b/variants/adafruit_feather_esp32s3/tinyuf2.bin index d76bd75c297..62d0b6b3ae9 100644 Binary files a/variants/adafruit_feather_esp32s3/tinyuf2.bin and b/variants/adafruit_feather_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin index 9cca9d28a05..199cdabc2c9 100644 Binary files a/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin b/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin index c345a5b8ce5..dda663678ac 100644 Binary files a/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin index 4e92bba7a71..e822c1f875a 100644 Binary files a/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin b/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin index 70f80cf1dd3..86345d6483b 100644 Binary files a/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin index 2babca98301..2ab3ebc0d18 100644 Binary files a/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin b/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin index 1f2b1d66157..035ba42a511 100644 Binary files a/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin differ diff --git a/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin index 7e153e6b117..e4146b191f8 100644 Binary files a/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_funhouse_esp32s2/tinyuf2.bin b/variants/adafruit_funhouse_esp32s2/tinyuf2.bin index 4cf847944f7..1e75fb3b404 100644 Binary files a/variants/adafruit_funhouse_esp32s2/tinyuf2.bin and b/variants/adafruit_funhouse_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin index eec06c84fa2..5f5afe53b87 100644 Binary files a/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_magtag29_esp32s2/pins_arduino.h b/variants/adafruit_magtag29_esp32s2/pins_arduino.h index 197f2e4c1aa..8e3d2c8e401 100644 --- a/variants/adafruit_magtag29_esp32s2/pins_arduino.h +++ b/variants/adafruit_magtag29_esp32s2/pins_arduino.h @@ -44,6 +44,7 @@ static const uint8_t BUTTON_D = PIN_BUTTON4; static const uint8_t LIGHT_SENSOR = 3; static const uint8_t BATT_MONITOR = 4; +#define BAT_VOLT_PIN BATT_MONITOR static const uint8_t SPEAKER_SHUTDOWN = 16; static const uint8_t SDA = 33; diff --git a/variants/adafruit_magtag29_esp32s2/tinyuf2.bin b/variants/adafruit_magtag29_esp32s2/tinyuf2.bin index 7a70be3ef32..5b1c92e019a 100644 Binary files a/variants/adafruit_magtag29_esp32s2/tinyuf2.bin and b/variants/adafruit_magtag29_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin index 6f062fea2fe..ed3a4aab55f 100644 Binary files a/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin b/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin index 16f7054546b..d671d114d61 100644 Binary files a/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin and b/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin index 1a98daf73f7..434a61ceaa9 100644 Binary files a/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s2/tinyuf2.bin b/variants/adafruit_metro_esp32s2/tinyuf2.bin index 6763a41df29..4350be6268c 100644 Binary files a/variants/adafruit_metro_esp32s2/tinyuf2.bin and b/variants/adafruit_metro_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin index 03cc2bfe4cf..0ef7720ad5b 100644 Binary files a/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s3/tinyuf2.bin b/variants/adafruit_metro_esp32s3/tinyuf2.bin index d0eaed3f7cf..aea78b0a4d3 100644 Binary files a/variants/adafruit_metro_esp32s3/tinyuf2.bin and b/variants/adafruit_metro_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin index f34e2710bd9..5ce1a3ba476 100644 Binary files a/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s2/tinyuf2.bin b/variants/adafruit_qtpy_esp32s2/tinyuf2.bin index 008c8a1f9c4..3ce657e272a 100644 Binary files a/variants/adafruit_qtpy_esp32s2/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin index 301ccf6088c..e82118ab400 100644 Binary files a/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin index 887fd31be65..2526253b577 100644 Binary files a/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin index ff0868213e8..d4e57a792e2 100644 Binary files a/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin index 5dfee90e6e5..328e67b1eb8 100644 Binary files a/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin differ diff --git a/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin b/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin index 0d437a3e3a3..7b6ba141439 100644 Binary files a/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin and b/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin b/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin index ed8e9df4bcd..9d7025242c9 100644 Binary files a/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin and b/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin differ diff --git a/variants/arduino_nesso_n1/pins_arduino.h b/variants/arduino_nesso_n1/pins_arduino.h new file mode 100644 index 00000000000..ce8a4db2900 --- /dev/null +++ b/variants/arduino_nesso_n1/pins_arduino.h @@ -0,0 +1,82 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303A +#define USB_PID 0x1001 +#define USB_MANUFACTURER "Arduino" +#define USB_PRODUCT "Nesso N1" +#define USB_SERIAL "" + +static const uint8_t TX = -1; +static const uint8_t RX = -1; + +static const uint8_t SDA = 10; +static const uint8_t SCL = 8; + +static const uint8_t MOSI = 21; +static const uint8_t MISO = 22; +static const uint8_t SCK = 20; +static const uint8_t SS = 23; + +static const uint8_t D1 = 7; +static const uint8_t D2 = 2; +static const uint8_t D3 = 6; + +static const uint8_t IR_TX_PIN = 9; +static const uint8_t BEEP_PIN = 11; + +static const uint8_t GROVE_IO_0 = 5; +static const uint8_t GROVE_IO_1 = 4; + +static const uint8_t LORA_IRQ = 15; +static const uint8_t LORA_CS = 23; +static const uint8_t LORA_BUSY = 19; + +static const uint8_t SYS_IRQ = 3; + +static const uint8_t LCD_CS = 17; +static const uint8_t LCD_RS = 16; + +#if !defined(MAIN_ESP32_HAL_GPIO_H_) && defined(__cplusplus) /* && !defined(ARDUINO_CORE_BUILD) */ + +#define ATTRIBUTE_ERROR __attribute__((error("Please include Arduino_Nesso_N1.h"))) + +class ExpanderPinError { +public: + ExpanderPinError(uint16_t p){}; +}; + +void ATTRIBUTE_ERROR pinMode(ExpanderPinError pin, uint8_t mode); +void ATTRIBUTE_ERROR digitalWrite(ExpanderPinError pin, uint8_t val); +int ATTRIBUTE_ERROR digitalRead(ExpanderPinError pin); + +extern ExpanderPinError _LORA_LNA_ENABLE; +extern ExpanderPinError _LORA_ANTENNA_SWITCH; +extern ExpanderPinError _LORA_ENABLE; +extern ExpanderPinError _POWEROFF; +extern ExpanderPinError _GROVE_POWER_EN; +extern ExpanderPinError _VIN_DETECT; +extern ExpanderPinError _LCD_RESET; +extern ExpanderPinError _LCD_BACKLIGHT; +extern ExpanderPinError _LED_BUILTIN; +extern ExpanderPinError _KEY1; +extern ExpanderPinError _KEY2; + +#define LORA_LNA_ENABLE _LORA_LNA_ENABLE +#define LORA_ANTENNA_SWITCH _LORA_ANTENNA_SWITCH +#define LORA_ENABLE _LORA_ENABLE +#define POWEROFF _POWEROFF +#define GROVE_POWER_EN _GROVE_POWER_EN +#define VIN_DETECT _VIN_DETECT +#define LCD_RESET _LCD_RESET +#define LCD_BACKLIGHT _LCD_BACKLIGHT +#define LED_BUILTIN _LED_BUILTIN +#define KEY1 _KEY1 +#define KEY2 _KEY2 + +#endif + +#endif /* Pins_Arduino_h */ diff --git a/variants/axiometa_pixie_m1/pins_arduino.h b/variants/axiometa_pixie_m1/pins_arduino.h new file mode 100644 index 00000000000..d318030decc --- /dev/null +++ b/variants/axiometa_pixie_m1/pins_arduino.h @@ -0,0 +1,70 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Pixie has a built in RGB LED WS2812B and a regular LED +#define PIN_RGB_LED 21 +#define PIN_LED 11 +// Regular built-in LED (pin 11) - for use with digitalWrite() +#define LED_BUILTIN PIN_LED +#define BUILTIN_LED LED_BUILTIN // backward compatibility +// RGB LED (pin 21) - use with RGB library functions +#define RGB_LED PIN_RGB_LED +// Allow testing for LED_BUILTIN +#ifdef LED_BUILTIN +// Defined and ready to use +#endif + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 10; +static const uint8_t SCL = 11; + +static const uint8_t SS = 1; +static const uint8_t MOSI = 12; +static const uint8_t MISO = 13; +static const uint8_t SCK = 14; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +#endif /* Pins_Arduino_h */ diff --git a/variants/cyobot_v2_esp32s3/pins_arduino.h b/variants/cyobot_v2_esp32s3/pins_arduino.h index 45f0968ef2a..d913251f1ec 100644 --- a/variants/cyobot_v2_esp32s3/pins_arduino.h +++ b/variants/cyobot_v2_esp32s3/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t BUTTON1 = 38; static const uint8_t LED = 24; static const uint8_t BAT_MEAS = 6; +#define BAT_VOLT_PIN BAT_MEAS static const uint8_t CHAR_DET = 23; static const uint8_t NEO_BASE = 7; diff --git a/variants/cytron_maker_feather_aiot_s3/pins_arduino.h b/variants/cytron_maker_feather_aiot_s3/pins_arduino.h index 9f7475294e8..566ae2544df 100644 --- a/variants/cytron_maker_feather_aiot_s3/pins_arduino.h +++ b/variants/cytron_maker_feather_aiot_s3/pins_arduino.h @@ -29,6 +29,7 @@ static const uint8_t RGB_BUILTIN = SOC_GPIO_PIN_COUNT + 46; // RGB LED. #define VIN 13 // Vin Sense. #define VBATT 13 +#define BAT_VOLT_PIN VBATT #define VOLTAGE_MONITOR 13 static const uint8_t TX = 15; diff --git a/variants/d1_mini32/pins_arduino.h b/variants/d1_mini32/pins_arduino.h index 826eff64104..12756295c37 100644 --- a/variants/d1_mini32/pins_arduino.h +++ b/variants/d1_mini32/pins_arduino.h @@ -8,6 +8,7 @@ static const uint8_t LED_BUILTIN = 2; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN static const uint8_t _VBAT = 35; // battery voltage +#define BAT_VOLT_PIN _VBAT #define PIN_WIRE_SDA SDA // backward compatibility #define PIN_WIRE_SCL SCL // backward compatibility diff --git a/variants/d32/pins_arduino.h b/variants/d32/pins_arduino.h index d5346c38acc..6b94d658624 100644 --- a/variants/d32/pins_arduino.h +++ b/variants/d32/pins_arduino.h @@ -8,5 +8,6 @@ static const uint8_t LED_BUILTIN = 5; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN static const uint8_t _VBAT = 35; // battery voltage +#define BAT_VOLT_PIN _VBAT #endif /* Pins_Arduino_h */ diff --git a/variants/d32_pro/pins_arduino.h b/variants/d32_pro/pins_arduino.h index fd3c899ef11..2e016362011 100644 --- a/variants/d32_pro/pins_arduino.h +++ b/variants/d32_pro/pins_arduino.h @@ -9,6 +9,7 @@ static const uint8_t LED_BUILTIN = 5; #define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN static const uint8_t _VBAT = 35; // battery voltage +#define BAT_VOLT_PIN _VBAT #define TF_CS 4 // TF (Micro SD Card) CS pin #define TS_CS 12 // Touch Screen CS pin diff --git a/variants/deneyapkart1Av2/pins_arduino.h b/variants/deneyapkart1Av2/pins_arduino.h index 141367aa4c4..7e1e4097b8f 100644 --- a/variants/deneyapkart1Av2/pins_arduino.h +++ b/variants/deneyapkart1Av2/pins_arduino.h @@ -102,5 +102,6 @@ static const uint8_t SDCS = 11; static const uint8_t SDCK = 13; static const uint8_t BAT = 9; +#define BAT_VOLT_PIN BAT #endif /* Pins_Arduino_h */ diff --git a/variants/deneyapkartv2/pins_arduino.h b/variants/deneyapkartv2/pins_arduino.h index f7eccadb13c..0167516f286 100644 --- a/variants/deneyapkartv2/pins_arduino.h +++ b/variants/deneyapkartv2/pins_arduino.h @@ -119,5 +119,6 @@ static const uint8_t SDCK = 13; static const uint8_t SDDA = 14; static const uint8_t BAT = 3; +#define BAT_VOLT_PIN BAT #endif /* Pins_Arduino_h */ diff --git a/variants/esp32c3-devkit-lipo/pins_arduino.h b/variants/esp32c3-devkit-lipo/pins_arduino.h index 5459b893f27..318882b2541 100644 --- a/variants/esp32c3-devkit-lipo/pins_arduino.h +++ b/variants/esp32c3-devkit-lipo/pins_arduino.h @@ -28,6 +28,7 @@ static const uint8_t SCK = 4; //static const uint8_t PWR_SENSE = 4; // battery measurement - disabled by default - check the schematic //static const uint8_t BAT_SENSE = 3; +// #define BAT_VOLT_PIN BAT_SENSE static const uint8_t A0 = 0; static const uint8_t A1 = 1; static const uint8_t A2 = 2; diff --git a/variants/esp32c61/pins_arduino.h b/variants/esp32c61/pins_arduino.h new file mode 100644 index 00000000000..dd25d0e53e7 --- /dev/null +++ b/variants/esp32c61/pins_arduino.h @@ -0,0 +1,37 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define PIN_RGB_LED 8 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 11; +static const uint8_t RX = 10; + +static const uint8_t SDA = 23; +static const uint8_t SCL = 22; + +static const uint8_t SS = 25; +static const uint8_t MOSI = 26; +static const uint8_t MISO = 27; +static const uint8_t SCK = 28; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; + +// LP I2C Pins are fixed on ESP32-C61 +#define WIRE1_PIN_DEFINED +static const uint8_t SDA1 = 6; +static const uint8_t SCL1 = 7; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32micromod/pins_arduino.h b/variants/esp32micromod/pins_arduino.h index ea74895edb2..24f6be19bd2 100644 --- a/variants/esp32micromod/pins_arduino.h +++ b/variants/esp32micromod/pins_arduino.h @@ -25,6 +25,7 @@ static const uint8_t SCK = 18; static const uint8_t A0 = 34; static const uint8_t A1 = 35; static const uint8_t BATT_VIN = 39; +#define BAT_VOLT_PIN BATT_VIN static const uint8_t PWM0 = 13; static const uint8_t PWM1 = 12; diff --git a/variants/esp32p4_4ds_mipi/pins_arduino.h b/variants/esp32p4_4ds_mipi/pins_arduino.h new file mode 100644 index 00000000000..d4a11090b0c --- /dev/null +++ b/variants/esp32p4_4ds_mipi/pins_arduino.h @@ -0,0 +1,73 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// Use default UART0 pins +static const uint8_t TX = 37; +static const uint8_t RX = 38; + +// Default pins (7 and 8) are used by on-board components already, +// for libraries, this can be set manually +// so let's keep the default for the user +static const uint8_t SDA = 2; // careful, also used as T0 pin +static const uint8_t SCL = 3; // careful, also used as T1 pin + +static const uint8_t SCK = 6; // careful, also used as T2 pin +static const uint8_t MOSI = 14; // careful, also used as T1 pin +static const uint8_t MISO = 15; // careful, also used as T0 pin +static const uint8_t SS = 16; // careful, also used as A9 pin + +static const uint8_t A0 = 21; +static const uint8_t A1 = 20; +static const uint8_t A2 = 19; +static const uint8_t A3 = 18; +static const uint8_t A4 = 17; +static const uint8_t A5 = 52; +static const uint8_t A6 = 51; +static const uint8_t A7 = 50; +static const uint8_t A8 = 49; +static const uint8_t A9 = 16; // careful, also used as SPI SS pin + +static const uint8_t T0 = 15; // careful, also used as SPI MISO pin +static const uint8_t T1 = 14; // careful, also used as SPI MOSI pin +static const uint8_t T2 = 6; // careful, also used as SPI SCK pin +static const uint8_t T3 = 3; // careful, also used as I2C SCL pin +static const uint8_t T4 = 2; // careful, also used as I2C SDA pin + +/* 4D Systems ESP32-P4 board specific definitions */ +// LCD +#define LCD_INTERFACE_MIPI + +#define LCD_BL_IO 22 +#define LCD_BL_ON_LEVEL 1 +#define LCD_BL_OFF_LEVEL !LCD_BL_ON_LEVEL + +#define LCD_RST_IO 23 +#define LCD_RST_ACTIVE_HIGH true + +// I2C for on-board components +#define I2C_SDA 7 +#define I2C_SCL 8 + +// Touch +#define CTP_RST 4 +#define CTP_INT 5 + +// Audio +#define AMP_CTRL 53 +#define I2S_DSDIN 9 +#define I2S_LRCK 10 +#define I2S_ASDOUT 11 +#define I2S_SCLK 12 +#define I2S_MCLK 13 + +// SDMMC +#define BOARD_HAS_SDMMC +#define BOARD_SDMMC_SLOT 0 +#define BOARD_SDMMC_POWER_CHANNEL 4 +#define BOARD_SDMMC_POWER_PIN 45 +#define BOARD_SDMMC_POWER_ON_LEVEL LOW + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32s2-devkit-lipo-usb/pins_arduino.h b/variants/esp32s2-devkit-lipo-usb/pins_arduino.h index 6dba09dbe43..207fa8a583a 100644 --- a/variants/esp32s2-devkit-lipo-usb/pins_arduino.h +++ b/variants/esp32s2-devkit-lipo-usb/pins_arduino.h @@ -27,6 +27,7 @@ static const uint8_t SCK = 36; //static const uint8_t PWR_SENSE = 7; // battery measurement - disabled by default - check the schematic //static const uint8_t BAT_SENSE = 8; +// #define BAT_VOLT_PIN BAT_SENSE static const uint8_t A0 = 1; static const uint8_t A1 = 2; diff --git a/variants/esp32s2-devkit-lipo/pins_arduino.h b/variants/esp32s2-devkit-lipo/pins_arduino.h index 98116754f5a..fed7bd63e73 100644 --- a/variants/esp32s2-devkit-lipo/pins_arduino.h +++ b/variants/esp32s2-devkit-lipo/pins_arduino.h @@ -29,6 +29,7 @@ static const uint8_t SCK = 36; //static const uint8_t PWR_SENSE = 7; // battery measurement - disabled by default - check the schematic //static const uint8_t BAT_SENSE = 8; +// #define BAT_VOLT_PIN BAT_SENSE static const uint8_t A0 = 1; static const uint8_t A1 = 2; diff --git a/variants/esp32s3-devkit-lipo/pins_arduino.h b/variants/esp32s3-devkit-lipo/pins_arduino.h index 3e1ae6f2381..7ff17f1a636 100644 --- a/variants/esp32s3-devkit-lipo/pins_arduino.h +++ b/variants/esp32s3-devkit-lipo/pins_arduino.h @@ -35,6 +35,7 @@ static const uint8_t SCK = 12; static const uint8_t PWR_SENSE = 5; // battery measurement static const uint8_t BAT_SENSE = 6; +#define BAT_VOLT_PIN BAT_SENSE static const uint8_t A0 = 1; static const uint8_t A1 = 2; diff --git a/variants/esp32s3usbotg/pins_arduino.h b/variants/esp32s3usbotg/pins_arduino.h index 5b873e2d2f1..13b9d245356 100644 --- a/variants/esp32s3usbotg/pins_arduino.h +++ b/variants/esp32s3usbotg/pins_arduino.h @@ -68,6 +68,7 @@ static const uint8_t T3 = 3; #define OVER_CURRENT 21 // Current overrun signal, high level means overrun. #define HOST_VOLTS 1 // USB_DEV voltage monitoring, ADC1 channel 0. actual_v = value_v * 3.7 #define BAT_VOLTS 2 // Battery voltage monitoring, ADC1 channel 1. actual_v = value_v * 2 +#define BAT_VOLT_PIN BAT_VOLTS // USB Port #define USB_DN 19 // USB D- diff --git a/variants/feather_esp32/pins_arduino.h b/variants/feather_esp32/pins_arduino.h index 523ea49f6ec..4078b789079 100644 --- a/variants/feather_esp32/pins_arduino.h +++ b/variants/feather_esp32/pins_arduino.h @@ -38,6 +38,7 @@ static const uint8_t A12 = 13; // vbat measure static const uint8_t BATT_MONITOR = 35; +#define BAT_VOLT_PIN BATT_MONITOR static const uint8_t A13 = 35; //static const uint8_t Ax = 0; // not used/available //static const uint8_t Ax = 2; // not used/available diff --git a/variants/fed4/pins_arduino.h b/variants/fed4/pins_arduino.h index f3741700ffa..46705a32639 100644 --- a/variants/fed4/pins_arduino.h +++ b/variants/fed4/pins_arduino.h @@ -51,6 +51,7 @@ static const uint8_t T6 = 6; static const uint8_t BOOT_BTN = 0; static const uint8_t VBAT_VOLTAGE = 7; +#define BAT_VOLT_PIN VBAT_VOLTAGE static const uint8_t LDO2 = 47; static const uint8_t STATUS_RGB = 35; static const uint8_t RGB_STRIP = 36; diff --git a/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin b/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin new file mode 100644 index 00000000000..b83265965f4 Binary files /dev/null and b/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin differ diff --git a/variants/fobe_quill_esp32s3_mesh/pins_arduino.h b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h new file mode 100644 index 00000000000..97e587ac0d0 --- /dev/null +++ b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h @@ -0,0 +1,108 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303A +#define USB_PID 0x82F4 +#define USB_MANUFACTURER "FoBE Studio" +#define USB_PRODUCT "FoBE Quill ESP32S3 Mesh" +#define USB_SERIAL "" // Empty string for MAC address + +// User LED +#define LED_BUILTIN 11 +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +/* + * Battery + */ +#define PIN_VBAT (10) +#define BAT_VOLT_PIN PIN_VBAT + +/* + * Buttons + */ +#define PIN_BUTTON1 (0) + +/* + * Serial interfaces + */ +static const uint8_t TX = 9; +static const uint8_t RX = 8; + +/* + * Wire Interfaces + */ +static const uint8_t SDA = 14; +static const uint8_t SCL = 13; + +/* + * SPI interfaces + */ +static const uint8_t SS = 45; +static const uint8_t MOSI = 39; +static const uint8_t SCK = 40; +static const uint8_t MISO = 41; + +/* + * Screen + */ +#define PIN_OLED_SDA (14) +#define PIN_OLED_SCL (13) +#define PIN_OLED_EN (12) + +/* + * LoRa + */ +#define PIN_SX126X_NSS (45) +#define PIN_SX126X_DIO1 (42) +#define PIN_SX126X_BUSY (43) +#define PIN_SX126X_RESET (44) +#define PIN_SX126X_TXEN (-1) +#define PIN_SX126X_RXEN (46) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* + * MFP + */ +#define PIN_MFP1 (38) +#define PIN_MFP2 (37) +#define PIN_MFP3 (36) +#define PIN_MFP4 (35) + +/* + * Power + */ +#define PIN_PERI_EN (1) + +/* + * PINs + */ +static const uint8_t A0 = 2; +static const uint8_t A1 = 3; +static const uint8_t A2 = 4; +static const uint8_t A3 = 5; +static const uint8_t A4 = 6; +static const uint8_t A5 = 7; +static const uint8_t D0 = 8; +static const uint8_t D1 = 9; +static const uint8_t D2 = 11; +static const uint8_t D3 = 38; +static const uint8_t D4 = 37; +static const uint8_t D5 = 36; +static const uint8_t D6 = 35; +static const uint8_t D7 = 34; +static const uint8_t D8 = 33; +static const uint8_t D9 = 47; +static const uint8_t D10 = 48; +static const uint8_t D11 = 21; +static const uint8_t D12 = 18; +static const uint8_t D13 = 17; +static const uint8_t MTCK = 39; +static const uint8_t MTDO = 40; +static const uint8_t MTDI = 41; +static const uint8_t MTMS = 42; + +#endif /* Pins_Arduino_h */ diff --git a/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin b/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin new file mode 100644 index 00000000000..b854b19518f Binary files /dev/null and b/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin differ diff --git a/variants/fobe_quill_esp32s3_mesh/variant.cpp b/variants/fobe_quill_esp32s3_mesh/variant.cpp new file mode 100644 index 00000000000..32ef46f053b --- /dev/null +++ b/variants/fobe_quill_esp32s3_mesh/variant.cpp @@ -0,0 +1,15 @@ +#include "esp32-hal-gpio.h" +#include "pins_arduino.h" + +extern "C" { + +void initVariant(void) { + // Turn on the peripheral power + pinMode(PIN_PERI_EN, OUTPUT); + digitalWrite(PIN_PERI_EN, HIGH); + + // Turn on the OLED power + pinMode(PIN_OLED_EN, OUTPUT); + digitalWrite(PIN_OLED_EN, LOW); +} +} diff --git a/variants/fri3d_2024_esp32s3/pins_arduino.h b/variants/fri3d_2024_esp32s3/pins_arduino.h index 3cdba371f57..7470daf518c 100644 --- a/variants/fri3d_2024_esp32s3/pins_arduino.h +++ b/variants/fri3d_2024_esp32s3/pins_arduino.h @@ -35,6 +35,7 @@ static const uint8_t SCK = 7; #define PIN_BLASTER 10 #define PIN_BUZZER 46 #define PIN_BATTERY 13 +#define BAT_VOLT_PIN PIN_BATTERY #define PIN_SDCARD_CS SS diff --git a/variants/heltec_capsule_sensor_v3/pins_arduino.h b/variants/heltec_capsule_sensor_v3/pins_arduino.h index 2a74e055599..7dd673f1ddf 100644 --- a/variants/heltec_capsule_sensor_v3/pins_arduino.h +++ b/variants/heltec_capsule_sensor_v3/pins_arduino.h @@ -76,6 +76,7 @@ static const uint8_t GPS_RESET_PIN = 3; static const uint8_t GPS_PPS_PIN = 1; static const uint8_t ADC_BATTERY_PIN = 7; +#define BAT_VOLT_PIN ADC_BATTERY_PIN static const uint8_t ADC_BATTERY_CTRL_PIN = 36; static const uint8_t RST_LoRa = 12; diff --git a/variants/kodedot/pins_arduino.h b/variants/kodedot/pins_arduino.h index f19ddb8a616..e6924435960 100644 --- a/variants/kodedot/pins_arduino.h +++ b/variants/kodedot/pins_arduino.h @@ -1,11 +1,11 @@ /* ──────────────────────────────────────────────────────────────────────── - KodeDot – ESP32-S3R8 Variant + KodeDot – ESP32-S3R8 Variant (rev. with OPI flash/PSRAM) Pin definition file for the Arduino-ESP32 core ──────────────────────────────────────────────────────────────────────── * External 2 × 10 connector → simple aliases PIN1 … PIN20 * On-board QSPI LCD 410×502 @40 MHz (SPI3_HOST) - * micro-SD on SPI2_HOST + * micro-SD on SDMMC (1-bit) * Dual-I²C: external (GPIO37/36) + internal-sensors (GPIO48/47) * USB VID/PID 0x303A:0x1001 */ @@ -33,13 +33,12 @@ static const uint8_t SDA = 36; // GPIO36 – PIN14 #define INT_I2C_SCL 47 // GPIO47 #define INT_I2C_SDA 48 // GPIO48 -/*──────────────── SPI2 – micro-SD ───────────────────────*/ -static const uint8_t SS = 15; // SD_CS -static const uint8_t MOSI = 16; // SD_MOSI -static const uint8_t MISO = 18; // SD_MISO -static const uint8_t SCK = 17; // SD_CLK -#define BOARD_HAS_SD_SPI -#define SD_CS SS +/*──────────────── SDMMC – micro-SD (1-bit) ───────────────*/ +#define BOARD_HAS_SD_SDMMC +#define SDMMC_CMD 5 // SD_CMD +#define SDMMC_CLK 6 // SD_CLK +#define SDMMC_D0 7 // SD_D0 +#define SD_CS -1 // No CS pin in SDMMC mode /*──────────────── QSPI LCD (SPI3_HOST) ─────────────────– * Controller: ST7789 / 4-line SPI (no D/C pin) @@ -52,16 +51,14 @@ static const uint8_t SCK = 17; // SD_CLK #define LCD_HEIGHT 502 #define LCD_HOST SPI3_HOST -#define LCD_SCK 35 // GPIO35 • QSPI_CLK -#define LCD_MOSI 33 // GPIO33 • QSPI_IO0 (D0) -#define LCD_IO1 34 // GPIO34 • QSPI_IO1 (D1) -#define LCD_IO2 37 // GPIO37 • QSPI_IO2 (D2) -#define LCD_IO3 36 // GPIO36 • QSPI_IO3 (D3) -#define LCD_CS 10 // GPIO10 -#define LCD_RST 9 // GPIO09 +#define LCD_SCK 17 // GPIO17 • QSPI_CLK +#define LCD_MOSI 15 // GPIO15 • QSPI_IO0 (D0) +#define LCD_IO1 14 // GPIO14 • QSPI_IO1 (D1) +#define LCD_IO2 16 // GPIO16 • QSPI_IO2 (D2) +#define LCD_IO3 10 // GPIO10 • QSPI_IO3 (D3) +#define LCD_CS 9 // GPIO09 +#define LCD_RST 8 // GPIO08 #define LCD_DC -1 // not used in 4-line SPI -/* Optional: back-light enable shares the NeoPixel pin */ -#define LCD_BL 5 // GPIO05 (same as NEOPIXEL) /*──────────────── Analog / Touch pads ────────────────*/ static const uint8_t A0 = 11; // PIN4 – GPIO11 / TOUCH11 / ADC2_CH0 @@ -71,9 +68,9 @@ static const uint8_t A3 = 14; // PIN10 – GPIO14 / TOUCH14 / ADC2_CH3 static const uint8_t T0 = A0, T1 = A1, T2 = A2, T3 = A3; /*──────────────── On-board controls & indicator ─────────*/ -#define BUTTON_TOP 0 // GPIO00 – BOOT • active-LOW -#define BUTTON_BOTTOM 6 // GPIO06 • active-LOW -#define NEOPIXEL_PIN 5 // GPIO05 – WS2812 +#define BUTTON_TOP 0 // GPIO00 – BOOT • active-LOW +#define BUTTON_BOTTOM -1 // via IO expander • no direct GPIO +#define NEOPIXEL_PIN 4 // GPIO04 – WS2812 #define LED_BUILTIN NEOPIXEL_PIN /*──────────────── JTAG (also on connector) ──────────────*/ @@ -104,4 +101,10 @@ static const uint8_t T0 = A0, T1 = A1, T2 = A2, T3 = A3; /* PIN1, PIN2, PIN19, PIN20 are power/ground and deliberately left undefined – they are **not** usable as GPIO. */ +/* ==== Default SPI pins for library compatibility ==== */ +#define SCK 17 // LCD_SCK • QSPI_CLK +#define MISO -1 // No MISO available in this design +#define MOSI 15 // LCD_MOSI • QSPI_IO0 +#define SS 9 // LCD_CS + #endif /* Pins_Arduino_h */ diff --git a/variants/lilygo_t3_s3_lr1121/pins_arduino.h b/variants/lilygo_t3_s3_lr1121/pins_arduino.h index 70fc3502dab..4259d4c592e 100644 --- a/variants/lilygo_t3_s3_lr1121/pins_arduino.h +++ b/variants/lilygo_t3_s3_lr1121/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t LED_BUILTIN = 37; static const uint8_t BUTTON_1 = 0; static const uint8_t BAT_VOLT = 1; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/lilygo_t3_s3_sx1262/pins_arduino.h b/variants/lilygo_t3_s3_sx1262/pins_arduino.h index 8fbf0b31066..8211473bdc8 100644 --- a/variants/lilygo_t3_s3_sx1262/pins_arduino.h +++ b/variants/lilygo_t3_s3_sx1262/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t LED_BUILTIN = 37; static const uint8_t BUTTON_1 = 0; static const uint8_t BAT_VOLT = 1; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/lilygo_t3_s3_sx127x/pins_arduino.h b/variants/lilygo_t3_s3_sx127x/pins_arduino.h index 1b3e0a99239..8b73a6b8385 100644 --- a/variants/lilygo_t3_s3_sx127x/pins_arduino.h +++ b/variants/lilygo_t3_s3_sx127x/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t LED_BUILTIN = 37; static const uint8_t BUTTON_1 = 0; static const uint8_t BAT_VOLT = 1; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; @@ -35,7 +36,6 @@ static const uint8_t SCK = 14; #define LORA_CS 7 // SX1276/SX1278 CS #define LORA_RST 8 // SX1276/SX1278 RST -#define LORA_BUSY 33 #define LORA_DIO0 9 //IRQ #define LORA_DIO1 33 #define LORA_DIO2 34 diff --git a/variants/lilygo_t3_s3_sx1280/pins_arduino.h b/variants/lilygo_t3_s3_sx1280/pins_arduino.h index ba342f3b8e9..567dd65817a 100644 --- a/variants/lilygo_t3_s3_sx1280/pins_arduino.h +++ b/variants/lilygo_t3_s3_sx1280/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t LED_BUILTIN = 37; static const uint8_t BUTTON_1 = 0; static const uint8_t BAT_VOLT = 1; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/lilygo_t3_s3_sx1280pa/pins_arduino.h b/variants/lilygo_t3_s3_sx1280pa/pins_arduino.h index 03212754a69..e6913208a35 100644 --- a/variants/lilygo_t3_s3_sx1280pa/pins_arduino.h +++ b/variants/lilygo_t3_s3_sx1280pa/pins_arduino.h @@ -12,6 +12,7 @@ static const uint8_t LED_BUILTIN = 37; static const uint8_t BUTTON_1 = 0; static const uint8_t BAT_VOLT = 1; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/lilygo_t_display/pins_arduino.h b/variants/lilygo_t_display/pins_arduino.h index 1caeffdfa5f..b560f641bb5 100644 --- a/variants/lilygo_t_display/pins_arduino.h +++ b/variants/lilygo_t_display/pins_arduino.h @@ -52,6 +52,7 @@ static const uint8_t DAC1 = 25; static const uint8_t DAC2 = 26; static const uint8_t VBAT = 34; +#define BAT_VOLT_PIN VBAT static const uint8_t RIGHT_BUTTON = 35; static const uint8_t LEFT_BUTTON = 0; diff --git a/variants/lilygo_t_display_s3/pins_arduino.h b/variants/lilygo_t_display_s3/pins_arduino.h index 8a179e67ef7..179076566a1 100644 --- a/variants/lilygo_t_display_s3/pins_arduino.h +++ b/variants/lilygo_t_display_s3/pins_arduino.h @@ -9,6 +9,7 @@ static const uint8_t BUTTON_1 = 0; static const uint8_t BUTTON_2 = 14; static const uint8_t BAT_VOLT = 4; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/lolin_c3_pico/pins_arduino.h b/variants/lolin_c3_pico/pins_arduino.h index 45087c3f9fc..781d6e6a0be 100644 --- a/variants/lolin_c3_pico/pins_arduino.h +++ b/variants/lolin_c3_pico/pins_arduino.h @@ -14,8 +14,9 @@ static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN // RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() -#define RGB_BUILTIN LED_BUILTIN -#define RGB_BRIGHTNESS 64 +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 +#define RGB_BUILTIN_LED_COLOR_ORDER LED_COLOR_ORDER_RGB static const uint8_t TX = 21; static const uint8_t RX = 20; @@ -24,6 +25,7 @@ static const uint8_t SDA = 8; static const uint8_t SCL = 10; static const uint8_t VBAT = 3; +#define BAT_VOLT_PIN VBAT static const uint8_t SCK = 1; static const uint8_t MISO = 0; diff --git a/variants/m5stack_atoms3/pins_arduino.h b/variants/m5stack_atoms3/pins_arduino.h index 9d2389cc98c..739cddf3acd 100644 --- a/variants/m5stack_atoms3/pins_arduino.h +++ b/variants/m5stack_atoms3/pins_arduino.h @@ -7,10 +7,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -// Some boards have too low voltage on this pin (board design bug) -// Use different pin with 3V and connect with 48 -// and change this setup for the chosen pin (for example 38) -static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + 48; +// The board RGB led is connected to GPIO #35 +#define PIN_RGB_LED 35 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN #define RGB_BUILTIN LED_BUILTIN diff --git a/variants/m5stack_tab5/pins_arduino.h b/variants/m5stack_tab5/pins_arduino.h new file mode 100644 index 00000000000..9f91ef7a29d --- /dev/null +++ b/variants/m5stack_tab5/pins_arduino.h @@ -0,0 +1,66 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// BOOT_MODE 35 +// BOOT_MODE2 36 pullup + +static const uint8_t TX = 37; +static const uint8_t RX = 38; + +static const uint8_t SDA = 53; +static const uint8_t SCL = 54; + +// Use GPIOs 36 or lower on the P4 DevKit to avoid LDO power issues with high numbered GPIOs. +static const uint8_t SS = 26; +static const uint8_t MOSI = 32; +static const uint8_t MISO = 33; +static const uint8_t SCK = 36; + +static const uint8_t A0 = 16; +static const uint8_t A1 = 17; +static const uint8_t A2 = 18; +static const uint8_t A3 = 19; +static const uint8_t A4 = 20; +static const uint8_t A5 = 21; +static const uint8_t A6 = 22; +static const uint8_t A7 = 23; +static const uint8_t A8 = 49; +static const uint8_t A9 = 50; +static const uint8_t A10 = 51; +static const uint8_t A11 = 52; +static const uint8_t A12 = 53; +static const uint8_t A13 = 54; + +static const uint8_t T0 = 2; +static const uint8_t T1 = 3; +static const uint8_t T2 = 4; +static const uint8_t T3 = 5; +static const uint8_t T4 = 6; +static const uint8_t T5 = 7; +static const uint8_t T6 = 8; +static const uint8_t T7 = 9; +static const uint8_t T8 = 10; +static const uint8_t T9 = 11; +static const uint8_t T10 = 12; +static const uint8_t T11 = 13; +static const uint8_t T12 = 14; +static const uint8_t T13 = 15; + +//SDMMC +#define BOARD_HAS_SDMMC +#define BOARD_SDMMC_SLOT 0 + +//WIFI - ESP32C6 +#define BOARD_HAS_SDIO_ESP_HOSTED +#define BOARD_SDIO_ESP_HOSTED_CLK 12 +#define BOARD_SDIO_ESP_HOSTED_CMD 13 +#define BOARD_SDIO_ESP_HOSTED_D0 11 +#define BOARD_SDIO_ESP_HOSTED_D1 10 +#define BOARD_SDIO_ESP_HOSTED_D2 9 +#define BOARD_SDIO_ESP_HOSTED_D3 8 +#define BOARD_SDIO_ESP_HOSTED_RESET 15 + +#endif /* Pins_Arduino_h */ diff --git a/variants/makergo_c6_supermini/pins_arduino.h b/variants/makergo_c6_supermini/pins_arduino.h new file mode 100644 index 00000000000..fb801ed94ff --- /dev/null +++ b/variants/makergo_c6_supermini/pins_arduino.h @@ -0,0 +1,101 @@ + +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" +/* + Arduino Pin Definitions for MakerGO ESP32 C6 SuperMini + +-----------------------------------------------------------------------------+ + | | | | # | | USB | | # | | | | + |:---:|:-------:|:------:|:--:|:--:|:---:|:--:|:--:|:--------:|:-------:|:---:| + | D16 | TX | GPIO16 | 1 | | TOP | | 20 | 5V | | | + | D17 | RX | GPIO17 | 2 | | | | 19 | GND | | | + | D0 | A0 | GPIO0 | 3 | | | | 18 | 3V3(OUT) | | | + | D1 | A1 | GPIO1 | 4 | | | | 17 | GPIO20 | SDA | D14 | + | D2 | A2 | GPIO2 | 5 | | | | 16 | GPIO19 | SCL | D12 | + | D3 | A3 | GPIO3 | 6 | | | | 15 | GPIO18 | | D11 | + | D4 | SS/A4 | GPIO4 | 7 | | 21 | | 14 | GPIO15 | LED | D13 | + | D5 | MOSI/A5 | GPIO5 | 8 | 23 | 22 | | 13 | GPIO14 | | D10 | + | D6 | MISO/A6 | GPIO6 | 9 | | | 24 | 12 | GPIO9 | BOOT | D9 | + | D7 | SCK | GPIO7 | 10 | | | 25 | 11 | GPIO8 | RGB_LED | D8 | + | | | | | | ↑ | | | | | | + +----------------------------------- | -------------------------------------+ + | + | | | | # | | | | | # | | | | + |:---:|:-------:|:------:|:--:|:--:|:---:|:--:|:--:|:--------:|:-------:|:---:| + | D19 | | GPIO21 | 21 | | | | | | | | + | D20 | | GPIO22 | 22 | | | | 24 | GPIO12 | | D15 | + | D21 | | GPIO23 | 23 | | | | 25 | GPIO13 | | D18 | + +-----------------------------------------------------------------------------+ +*/ +// The built-in RGB LED is connected to this pin +static const uint8_t PIN_RGB_LED = 8; +#define PIN_RGB_LED PIN_RGB_LED // allow testing #ifdef PIN_RGB_LED + +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +// but also used in new Arduino API rgbLedWrite() +static const uint8_t RGB_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define RGB_BUILTIN RGB_BUILTIN // allow testing #ifdef RGB_BUILTIN + +// Define default brightness for the built-in RGB LED +#define RGB_BRIGHTNESS 32 // default brightness level (0-255) + +// Define the color order for the built-in RGB LED +#define RGB_BUILTIN_LED_COLOR_ORDER LED_COLOR_ORDER_GRB // default WS2812B color order + +// Define the built-in LED pin (blue LED) +static const uint8_t LED_BUILTIN = 15; +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 16; +static const uint8_t RX = 17; + +static const uint8_t SDA = 20; +static const uint8_t SCL = 19; + +static const uint8_t SS = 4; +static const uint8_t MOSI = 5; +static const uint8_t MISO = 6; +static const uint8_t SCK = 7; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; // Note: A4 overlaps with SS +static const uint8_t A5 = 5; // Note: A5 overlaps with MOSI +static const uint8_t A6 = 6; // Note: A6 overlaps with MISO or SDA1 + +static const uint8_t D0 = 0; +static const uint8_t D1 = 1; +static const uint8_t D2 = 2; +static const uint8_t D3 = 3; +static const uint8_t D4 = 4; // Note: D4 overlaps with SS +static const uint8_t D5 = 5; // Note: D5 overlaps with MOSI +static const uint8_t D6 = 6; // Note: D6 overlaps with MISO or SDA1 +static const uint8_t D7 = 7; +static const uint8_t D8 = 8; // Note: D8 overlaps with PIN_RGB_LED +static const uint8_t D9 = 9; +static const uint8_t D10 = 14; +static const uint8_t D11 = 18; +static const uint8_t D12 = 19; // Note: D12 overlaps with SCL +static const uint8_t D13 = 15; // Note: D13 overlaps with LED_BUILTIN +static const uint8_t D14 = 20; // Note: D14 overlaps with SDA +static const uint8_t D15 = 12; +static const uint8_t D16 = 16; // Note: D16 overlaps with TX +static const uint8_t D17 = 17; // Note: D17 overlaps with RX +static const uint8_t D18 = 13; +static const uint8_t D19 = 21; +static const uint8_t D20 = 22; +static const uint8_t D21 = 23; + +// LP I2C Pins are fixed on ESP32-C6 +#define WIRE1_PIN_DEFINED +static const uint8_t SDA1 = 6; +static const uint8_t SCL1 = 7; + +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define BUILTIN_RGB RGB_BUILTIN // backward compatibility + +#endif /* Pins_Arduino_h */ diff --git a/variants/mant1s/pins_arduino.h b/variants/mant1s/pins_arduino.h new file mode 100644 index 00000000000..b0b7e54ffaa --- /dev/null +++ b/variants/mant1s/pins_arduino.h @@ -0,0 +1,38 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +static const uint8_t TX = 1; +static const uint8_t RX = 3; + +static const uint8_t SCL = 32; +static const uint8_t SDA = 33; + +static const uint8_t SS = 15; +static const uint8_t MOSI = 13; +static const uint8_t MISO = 12; +static const uint8_t SCK = 14; + +static const uint8_t A0 = 36; +static const uint8_t A1 = 37; +static const uint8_t A2 = 38; +static const uint8_t A3 = 39; +static const uint8_t A6 = 34; +static const uint8_t A7 = 35; + +static const uint8_t T0 = 4; +static const uint8_t T2 = 2; +static const uint8_t T3 = 15; +static const uint8_t T4 = 13; +static const uint8_t T5 = 12; +static const uint8_t T6 = 14; + +#define ETH_PHY_ADDR 0 +#define ETH_PHY_POWER -1 +#define ETH_PHY_MDC 8 +#define ETH_PHY_MDIO 7 +#define ETH_PHY_TYPE ETH_PHY_LAN867X +#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN + +#endif /* Pins_Arduino_h */ diff --git a/variants/oroca_edubot/pins_arduino.h b/variants/oroca_edubot/pins_arduino.h index b9129485beb..3022bcfbddc 100644 --- a/variants/oroca_edubot/pins_arduino.h +++ b/variants/oroca_edubot/pins_arduino.h @@ -35,6 +35,7 @@ static const uint8_t D8 = 33; // vbat measure static const uint8_t VBAT = 35; +#define BAT_VOLT_PIN VBAT static const uint8_t T0 = 4; static const uint8_t T1 = 0; diff --git a/variants/rakwireless_rak3112/pins_arduino.h b/variants/rakwireless_rak3112/pins_arduino.h index f1bcc7a6120..ee26aa570b9 100644 --- a/variants/rakwireless_rak3112/pins_arduino.h +++ b/variants/rakwireless_rak3112/pins_arduino.h @@ -19,6 +19,7 @@ static const uint8_t LED_BUILTIN = LED_GREEN; #define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN static const uint8_t BAT_VOLT = 21; +#define BAT_VOLT_PIN BAT_VOLT static const uint8_t TX = 43; static const uint8_t RX = 44; diff --git a/variants/roboheart_hercules/pins_arduino.h b/variants/roboheart_hercules/pins_arduino.h index aaa91f75799..e4777163142 100644 --- a/variants/roboheart_hercules/pins_arduino.h +++ b/variants/roboheart_hercules/pins_arduino.h @@ -29,13 +29,14 @@ #define TXD1 17 // GSM Vela connector board pins -#define GSM_PWRKEY 12 -#define GSM_DTR 13 -#define GSM_CTS 15 -#define GSM_RTS 14 -#define GSM_TX TXD1 -#define GSM_RX RXD1 -#define BATTERY_PIN 36 // Battery ADC pin +#define GSM_PWRKEY 12 +#define GSM_DTR 13 +#define GSM_CTS 15 +#define GSM_RTS 14 +#define GSM_TX TXD1 +#define GSM_RX RXD1 +#define BATTERY_PIN 36 // Battery ADC pin +#define BAT_VOLT_PIN BATTERY_PIN static const uint8_t TX = 35; static const uint8_t RX = 34; diff --git a/variants/sensebox_eye/APOTA.bin b/variants/sensebox_eye/APOTA.bin new file mode 100644 index 00000000000..18547ceb255 Binary files /dev/null and b/variants/sensebox_eye/APOTA.bin differ diff --git a/variants/sensebox_eye/APOTA.ino b/variants/sensebox_eye/APOTA.ino new file mode 100644 index 00000000000..58c1116cc70 --- /dev/null +++ b/variants/sensebox_eye/APOTA.ino @@ -0,0 +1,301 @@ +// APOTA is an Arduino fallback sketch that is written to OTA1_Partition. +// APOTA opens an access point which waits to receive a .bin file on /sketch. +// After successful upload, the file is written to OTA0_Partition, and the microcontroller reboots to the newly uploaded sketch. + +#define DISPLAY_ENABLED + +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_ENABLED +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +#include +#include +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +#include +Adafruit_NeoPixel rgb_led = Adafruit_NeoPixel(1, PIN_LED, NEO_GRB + NEO_KHZ800); + +#endif +#include "esp_partition.h" +#include "esp_ota_ops.h" +#include "esp_system.h" + +String ssid; +uint8_t mac[6]; + +// Create an instance of the server +WebServer server(80); +bool displayEnabled; + +const int BUTTON_PIN = 0; // GPIO for the button +volatile unsigned long lastPressTime = 0; // Time of last button press +volatile bool doublePressDetected = false; // Flag for double press +const unsigned long doublePressInterval = 500; // Max. time (in ms) between two presses for double press +volatile int pressCount = 0; // Counts the button presses + +const unsigned char epd_bitmap_wifi[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0xff, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x7c, 0x00, 0x03, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xf0, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x78, 0x00, + 0x03, 0xc0, 0x00, 0x00, 0x38, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x7f, 0xe0, 0x0e, 0x00, + 0x0c, 0x01, 0xff, 0xf0, 0x06, 0x00, 0x00, 0x07, 0xff, 0xfc, 0x02, 0x00, 0x00, 0x0f, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x07, 0x80, 0x00, 0x00, 0x38, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x01, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x00, 0x00, 0x00, 0x03, 0x80, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// 'checkmark', 44x44px +const unsigned char epd_bitmap_checkmark[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x00, + 0x00, 0x0f, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0x83, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xc7, 0x80, 0x00, 0x00, 0x00, 0x03, 0xef, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void IRAM_ATTR handleButtonPress() { + unsigned long currentTime = millis(); // Get current time + + // Debounce: If the current press is too close to the last one, ignore it + if (currentTime - lastPressTime > 50) { + pressCount++; // Count the button press + + // Check if this is the second press within the double-press interval + if (pressCount == 2 && (currentTime - lastPressTime <= doublePressInterval)) { + doublePressDetected = true; // Double press detected + pressCount = 0; // Reset counter + } + + lastPressTime = currentTime; // Update the time of the last press + } +} + +// Function to switch the boot partition to OTA0 +void setBootPartitionToOTA0() { + const esp_partition_t *ota0_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); + + if (ota0_partition) { + // Set OTA0 as new boot partition + esp_ota_set_boot_partition(ota0_partition); + Serial.println("Boot partition changed to OTA0. Restarting..."); + + // Restart to boot from the new partition + esp_restart(); + } else { + Serial.println("OTA0 partition not found!"); + } +} + +void setupDisplay() { + Wire.begin(PIN_QWIIC_SDA, PIN_QWIIC_SCL); + displayEnabled = Wire.requestFrom(0x3D, 1); // Check if the display is connected + if (displayEnabled) { + display.begin(SSD1306_SWITCHCAPVCC, 0x3D); + display.display(); + delay(100); + display.clearDisplay(); + } +} + +void displayStatusBar(int progress) { + display.clearDisplay(); + display.setCursor(16, 8); + display.println("Sketch is being"); + display.setCursor(32, 22); + display.println("uploaded!"); + + display.fillRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, BLACK); // Clear status bar area + display.drawRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, WHITE); // Draw border + int filledWidth = (progress * SCREEN_WIDTH - 4) / 100; // Calculate progress width + display.fillRect(1, SCREEN_HEIGHT - 23, filledWidth - 4, 6, WHITE); // Fill progress bar + + display.setCursor((SCREEN_WIDTH / 2) - 12, SCREEN_HEIGHT - 10); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.print(progress); + display.println(" %"); + display.display(); +} + +void displayWelcomeScreen() { + display.clearDisplay(); + + // Draw WiFi symbol + display.drawBitmap(0, 12, epd_bitmap_wifi, 44, 44, WHITE); + + // Display SSID text + display.setCursor(52, 13); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println("Connect"); + display.setCursor(60, 27); + display.println("with:"); + + // Display SSID + display.setCursor(40, 43); + display.setTextSize(1); // Larger text for SSID + display.print(ssid); + + display.display(); +} + +void displaySuccessScreen() { + display.clearDisplay(); + + // Draw WiFi symbol + display.drawBitmap(0, 12, epd_bitmap_checkmark, 44, 44, WHITE); + + // Display SSID text + display.setCursor(48, 22); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println("Successfully"); + display.setCursor(48, 36); + display.println("uploaded!"); + + display.display(); +} + +void wipeDisplay() { + display.clearDisplay(); + display.println(""); + display.display(); +} + +void setupWiFi() { + WiFi.macAddress(mac); + char macLastFour[5]; + snprintf(macLastFour, sizeof(macLastFour), "%02X%02X", mac[4], mac[5]); + ssid = "senseBox:" + String(macLastFour); + + // Define the IP address, gateway, and subnet mask + IPAddress local_IP(192, 168, 1, 1); // The new IP address + IPAddress gateway(192, 168, 1, 1); // Gateway address (can be the same as the AP's IP) + IPAddress subnet(255, 255, 255, 0); // Subnet mask + + // Set the IP address, gateway, and subnet mask of the access point + WiFi.softAPConfig(local_IP, gateway, subnet); + + // Start the access point + WiFi.softAP(ssid.c_str()); +} + +void setupOTA() { + // Handle updating process + server.on( + "/sketch", HTTP_POST, + []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + ESP.restart(); + }, + []() { + HTTPUpload &upload = server.upload(); + + if (upload.status == UPLOAD_FILE_START) { + Serial.setDebugOutput(true); + size_t fsize = UPDATE_SIZE_UNKNOWN; + if (server.clientContentLength() > 0) { + fsize = server.clientContentLength(); + } + Serial.printf("Receiving Update: %s, Size: %d\n", upload.filename.c_str(), fsize); + + Serial.printf("Update: %s\n", upload.filename.c_str()); + if (!Update.begin(fsize)) { //start with max available size + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } else { + int progress = (Update.progress() * 100) / Update.size(); + if (displayEnabled) { + displayStatusBar(progress); // Update progress on display + } + rgb_led.setPixelColor(0, rgb_led.Color(255, 255, 51)); + rgb_led.show(); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { //true to set the size to the current progress + if (displayEnabled) { + displaySuccessScreen(); + delay(3000); + wipeDisplay(); + } + rgb_led.setPixelColor(0, rgb_led.Color(51, 51, 255)); + rgb_led.show(); + } else { + Update.printError(Serial); + } + Serial.setDebugOutput(false); + } + yield(); + } + ); +} + +void setup() { + // Start Serial communication + Serial.begin(115200); + rgb_led.begin(); + rgb_led.setBrightness(15); + rgb_led.setPixelColor(0, rgb_led.Color(51, 51, 255)); + rgb_led.show(); + + // Configure button pin as input + pinMode(BUTTON_PIN, INPUT_PULLUP); + + // Interrupt for the button + attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING); + +#ifdef DISPLAY_ENABLED + setupDisplay(); +#endif + setupWiFi(); + // Set the ESP32 as an access point + setupOTA(); + server.begin(); +} + +void loop() { + // Handle client requests + server.handleClient(); + +#ifdef DISPLAY_ENABLED + if (displayEnabled) { + displayWelcomeScreen(); + } +#endif + + if (doublePressDetected) { + Serial.println("Double press detected!"); + setBootPartitionToOTA0(); +#ifdef DISPLAY_ENABLED + if (displayEnabled) { + display.setCursor(0, 0); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println(""); + display.display(); + delay(50); + } +#endif + // Restart to boot from the new partition + esp_restart(); + } +} diff --git a/variants/sensebox_eye/variant.cpp b/variants/sensebox_eye/variant.cpp index 4d6e38e73e0..cd86cdec84c 100644 --- a/variants/sensebox_eye/variant.cpp +++ b/variants/sensebox_eye/variant.cpp @@ -1,12 +1,39 @@ #include "esp32-hal-gpio.h" #include "pins_arduino.h" +#include "esp_log.h" +#include "esp_partition.h" +#include "esp_system.h" +#include "esp_ota_ops.h" extern "C" { -void initVariant(void) { - // blink the RGB LED - rgbLedWrite(PIN_LED, 0x00, 0x10, 0x00); // green +void blinkLED(uint8_t r, uint8_t g, uint8_t b) { + rgbLedWrite(PIN_LED, r, g, b); delay(20); rgbLedWrite(PIN_LED, 0x00, 0x00, 0x00); // off } + +void initVariant(void) { + // define button pin + pinMode(47, INPUT_PULLUP); + + // Check if button is pressed + if (digitalRead(47) == LOW) { + // When the button is pressed and then released, boot into the OTA1 partition + const esp_partition_t *ota1_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL); + + if (ota1_partition) { + esp_err_t err = esp_ota_set_boot_partition(ota1_partition); + if (err == ESP_OK) { + blinkLED(0x00, 0x00, 0x10); // blue + esp_restart(); // restart, to boot OTA1 partition + } else { + blinkLED(0x10, 0x00, 0x00); // red + ESP_LOGE("OTA", "Error setting OTA1 partition: %s", esp_err_to_name(err)); + } + } + } else { + blinkLED(0x00, 0x10, 0x00); // green + } +} } diff --git a/variants/sensebox_mcu_esp32s2/APOTA.bin b/variants/sensebox_mcu_esp32s2/APOTA.bin index 0ea39335dce..ebe676c1733 100644 Binary files a/variants/sensebox_mcu_esp32s2/APOTA.bin and b/variants/sensebox_mcu_esp32s2/APOTA.bin differ diff --git a/variants/sensebox_mcu_esp32s2/APOTA.ino b/variants/sensebox_mcu_esp32s2/APOTA.ino index 67348d0b20f..ef0fa7d9063 100644 --- a/variants/sensebox_mcu_esp32s2/APOTA.ino +++ b/variants/sensebox_mcu_esp32s2/APOTA.ino @@ -111,10 +111,10 @@ void setupDisplay() { void displayStatusBar(int progress) { display.clearDisplay(); - display.setCursor(24, 8); - display.println("Sketch wird"); - display.setCursor(22, 22); - display.println("hochgeladen!"); + display.setCursor(16, 8); + display.println("Sketch is being"); + display.setCursor(32, 22); + display.println("uploaded!"); display.fillRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, BLACK); // Clear status bar area display.drawRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, WHITE); // Draw border @@ -136,12 +136,12 @@ void displayWelcomeScreen() { display.drawBitmap(0, 12, epd_bitmap_wifi, 44, 44, WHITE); // Display SSID text - display.setCursor(40, 13); + display.setCursor(52, 13); display.setTextSize(1); display.setTextColor(WHITE, BLACK); - display.println("Verbinde dich"); // "Connect" + display.println("Connect"); display.setCursor(60, 27); - display.println("mit:"); // "with" + display.println("with:"); // Display SSID display.setCursor(40, 43); @@ -161,9 +161,9 @@ void displaySuccessScreen() { display.setCursor(48, 22); display.setTextSize(1); display.setTextColor(WHITE, BLACK); - display.println("Erfolgreich"); // "Successfully" + display.println("Successfully"); display.setCursor(48, 36); - display.println("hochgeladen!"); // "uploaded!" + display.println("uploaded!"); display.display(); } @@ -271,7 +271,7 @@ void loop() { #endif if (doublePressDetected) { - Serial.println("Doppeldruck erkannt!"); // "Double press detected!" + Serial.println("Double press detected!"); setBootPartitionToOTA0(); #ifdef DISPLAY_ENABLED display.setCursor(0, 0); diff --git a/variants/soldered_nula_deepsleep_esp32s3/pins_arduino.h b/variants/soldered_nula_deepsleep_esp32s3/pins_arduino.h new file mode 100644 index 00000000000..9819aa4f10b --- /dev/null +++ b/variants/soldered_nula_deepsleep_esp32s3/pins_arduino.h @@ -0,0 +1,56 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x82fc + +#define PIN_RGB_LED 2 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN LED_BUILTIN +#define RGB_BRIGHTNESS 64 + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 10; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 13; +static const uint8_t SCK = 12; + +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +#endif /* Pins_Arduino_h */ diff --git a/variants/soldered_nula_mini_esp32c6/pins_arduino.h b/variants/soldered_nula_mini_esp32c6/pins_arduino.h new file mode 100644 index 00000000000..058ebdd3822 --- /dev/null +++ b/variants/soldered_nula_mini_esp32c6/pins_arduino.h @@ -0,0 +1,33 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = 23; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t USER_BTN = 9; + +static const uint8_t TX = 16; +static const uint8_t RX = 17; + +static const uint8_t SDA = 6; +static const uint8_t SCL = 7; + +static const uint8_t SS = 2; +static const uint8_t MOSI = 3; +static const uint8_t MISO = 4; +static const uint8_t SCK = 5; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; +static const uint8_t A5 = 5; +static const uint8_t A6 = 6; + +#endif /* Pins_Arduino_h */ diff --git a/variants/tamc_termod_s3/pins_arduino.h b/variants/tamc_termod_s3/pins_arduino.h index 89d0b5107ae..4e989c88e92 100644 --- a/variants/tamc_termod_s3/pins_arduino.h +++ b/variants/tamc_termod_s3/pins_arduino.h @@ -57,6 +57,7 @@ static const uint8_t T13 = 13; static const uint8_t T14 = 14; static const uint8_t BAT_LV = 1; +#define BAT_VOLT_PIN BAT_LV static const uint8_t CHG = 2; static const uint8_t TFT_CS = 10; static const uint8_t TFT_DC = 18; diff --git a/variants/thingpulse_epulse_feather/pins_arduino.h b/variants/thingpulse_epulse_feather/pins_arduino.h index 62855db018f..92a421ebd56 100644 --- a/variants/thingpulse_epulse_feather/pins_arduino.h +++ b/variants/thingpulse_epulse_feather/pins_arduino.h @@ -32,6 +32,7 @@ static const uint8_t A12 = 13; // vbat measure static const uint8_t BATT_MONITOR = 35; // Note: voltage divider 2.2M/4.7M +#define BAT_VOLT_PIN BATT_MONITOR static const uint8_t A13 = 35; //static const uint8_t Ax = 0; // not used/available //static const uint8_t Ax = 2; // not used/available? GPIO02 is available! diff --git a/variants/ttgo-t-oi-plus/pins_arduino.h b/variants/ttgo-t-oi-plus/pins_arduino.h index 5738ce0277d..e34c92106fd 100644 --- a/variants/ttgo-t-oi-plus/pins_arduino.h +++ b/variants/ttgo-t-oi-plus/pins_arduino.h @@ -23,5 +23,6 @@ static const uint8_t A2 = 4; static const uint8_t A3 = 5; static const uint8_t BAT_ADC_PIN = 2; +#define BAT_VOLT_PIN BAT_ADC_PIN #endif /* Pins_Arduino_h */ diff --git a/variants/twinaiot/pins_arduino.h b/variants/twinaiot/pins_arduino.h new file mode 100644 index 00000000000..a08f3b7d455 --- /dev/null +++ b/variants/twinaiot/pins_arduino.h @@ -0,0 +1,54 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define RGB_PIN 15 + +static const uint8_t LED_BUILTIN = 35; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN RGB_PIN + +static const uint8_t TX = 39; +static const uint8_t RX = 40; + +static const uint8_t SDA = 39; +static const uint8_t SCL = 40; + +static const uint8_t SS = 1; +static const uint8_t MOSI = 2; +static const uint8_t MISO = 3; +static const uint8_t SCK = 4; + +static const uint8_t D6_OUT_PIN = 35; +static const uint8_t D9_OUT_PIN = 36; +static const uint8_t D10_OUT_PIN = 10; + +static const uint8_t TOUCH_PIN = 13; + +static const uint8_t TRIG_PIN = 5; // GPIO connected to HC-SR04 TRIG +static const uint8_t ECHO_PIN = 6; // GPIO connected to HC-SR04 ECHO + +static const uint8_t latchPin = 34; +static const uint8_t clockPin = 47; +static const uint8_t dataPin = 48; + +static const uint8_t D_IN_4 = 8; +static const uint8_t D_IN_8 = 11; +static const uint8_t D_IN_12 = 9; + +static const uint8_t AN_IN_4 = 17; +static const uint8_t AN_IN_8 = 16; +static const uint8_t AN_IN_12 = 7; + +static const uint8_t S1pin = 37; +static const uint8_t S2pin = 38; +static const uint8_t S3pin = 14; + +#endif /* Pins_Arduino_h */ diff --git a/variants/uPesy_esp32c3_basic/pins_arduino.h b/variants/uPesy_esp32c3_basic/pins_arduino.h index 7536acec2c8..6dcabdfd1b0 100644 --- a/variants/uPesy_esp32c3_basic/pins_arduino.h +++ b/variants/uPesy_esp32c3_basic/pins_arduino.h @@ -28,5 +28,6 @@ static const uint8_t A4 = 4; static const uint8_t A5 = 5; static const uint8_t VBAT_SENSE = 0; +#define BAT_VOLT_PIN VBAT_SENSE #endif /* Pins_Arduino_h */ diff --git a/variants/uPesy_esp32c3_mini/pins_arduino.h b/variants/uPesy_esp32c3_mini/pins_arduino.h index 71029338133..a2c3e1f9983 100644 --- a/variants/uPesy_esp32c3_mini/pins_arduino.h +++ b/variants/uPesy_esp32c3_mini/pins_arduino.h @@ -27,5 +27,6 @@ static const uint8_t A4 = 4; static const uint8_t A5 = 5; static const uint8_t VBAT_SENSE = 0; +#define BAT_VOLT_PIN VBAT_SENSE #endif /* Pins_Arduino_h */ diff --git a/variants/um_bling/pins_arduino.h b/variants/um_bling/pins_arduino.h index 590eec5efea..0e09f467457 100644 --- a/variants/um_bling/pins_arduino.h +++ b/variants/um_bling/pins_arduino.h @@ -52,6 +52,7 @@ static const uint8_t BUTTON_C = 33; static const uint8_t BUTTON_D = 34; static const uint8_t VBAT_SENSE = 17; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 16; static const uint8_t I2S_MIC_SEL = 39; diff --git a/variants/um_feathers2neo/pins_arduino.h b/variants/um_feathers2neo/pins_arduino.h index 92c9cd1a099..08434f541a9 100644 --- a/variants/um_feathers2neo/pins_arduino.h +++ b/variants/um_feathers2neo/pins_arduino.h @@ -65,6 +65,7 @@ static const uint8_t LED_BUILTIN = RGB_BUILTIN; static const uint8_t NEOPIXEL_PWR = 39; static const uint8_t VBAT_SENSE = 2; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 34; #endif /* Pins_Arduino_h */ diff --git a/variants/um_feathers3/pins_arduino.h b/variants/um_feathers3/pins_arduino.h index 1c81339c88e..594d9796983 100644 --- a/variants/um_feathers3/pins_arduino.h +++ b/variants/um_feathers3/pins_arduino.h @@ -54,6 +54,7 @@ static const uint8_t T12 = 12; static const uint8_t T14 = 14; static const uint8_t VBAT_SENSE = 2; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 34; // User LED diff --git a/variants/um_feathers3neo/pins_arduino.h b/variants/um_feathers3neo/pins_arduino.h index 94d546d22c2..de177c0f57d 100644 --- a/variants/um_feathers3neo/pins_arduino.h +++ b/variants/um_feathers3neo/pins_arduino.h @@ -50,6 +50,7 @@ static const uint8_t T12 = 12; static const uint8_t T14 = 14; static const uint8_t VBAT_SENSE = 2; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 15; // User LED diff --git a/variants/um_pros3/pins_arduino.h b/variants/um_pros3/pins_arduino.h index 4b9bc8de6aa..59600e74f4a 100644 --- a/variants/um_pros3/pins_arduino.h +++ b/variants/um_pros3/pins_arduino.h @@ -52,6 +52,7 @@ static const uint8_t T13 = 13; static const uint8_t T14 = 14; static const uint8_t VBAT_SENSE = 10; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 33; static const uint8_t RGB_DATA = 18; diff --git a/variants/um_tinyc6/pins_arduino.h b/variants/um_tinyc6/pins_arduino.h index 6505e1ed50e..21e4f42d140 100644 --- a/variants/um_tinyc6/pins_arduino.h +++ b/variants/um_tinyc6/pins_arduino.h @@ -44,6 +44,7 @@ static const uint8_t T8 = 8; static const uint8_t T9 = 9; static const uint8_t VBAT_SENSE = 4; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 10; static const uint8_t RGB_DATA = 23; diff --git a/variants/um_tinys2/pins_arduino.h b/variants/um_tinys2/pins_arduino.h index 2a6e03aa078..b72215c3b4a 100644 --- a/variants/um_tinys2/pins_arduino.h +++ b/variants/um_tinys2/pins_arduino.h @@ -63,6 +63,7 @@ static const uint8_t DAC1 = 17; static const uint8_t DAC2 = 18; static const uint8_t VBAT_SENSE = 3; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 21; static const uint8_t RGB_DATA = 1; diff --git a/variants/um_tinys3/pins_arduino.h b/variants/um_tinys3/pins_arduino.h index 24742781dce..7bc0d8055c4 100644 --- a/variants/um_tinys3/pins_arduino.h +++ b/variants/um_tinys3/pins_arduino.h @@ -44,6 +44,7 @@ static const uint8_t T8 = 8; static const uint8_t T9 = 9; static const uint8_t VBAT_SENSE = 10; +#define BAT_VOLT_PIN VBAT_SENSE static const uint8_t VBUS_SENSE = 33; static const uint8_t RGB_DATA = 18; diff --git a/variants/watchy/pins_arduino.h b/variants/watchy/pins_arduino.h index c126b27aa06..edb93649228 100644 --- a/variants/watchy/pins_arduino.h +++ b/variants/watchy/pins_arduino.h @@ -29,24 +29,23 @@ static const uint8_t RTC_INT_PIN = 27; #if defined(ARDUINO_WATCHY_V10) static const uint8_t UP_BTN_PIN = 32; static const uint8_t BATT_ADC_PIN = 33; -#define UP_BTN_MASK GPIO_SEL_32 -#define RTC_TYPE 1 //DS3231 +#define BAT_VOLT_PIN BATT_ADC_PIN +#define RTC_TYPE 1 //DS3231 #elif defined(ARDUINO_WATCHY_V15) static const uint8_t UP_BTN_PIN = 32; static const uint8_t BATT_ADC_PIN = 35; -#define UP_BTN_MASK GPIO_SEL_32 -#define RTC_TYPE 2 //PCF8563 +#define RTC_TYPE 2 //PCF8563 #elif defined(ARDUINO_WATCHY_V20) static const uint8_t UP_BTN_PIN = 35; static const uint8_t BATT_ADC_PIN = 34; -#define UP_BTN_MASK GPIO_SEL_35 -#define RTC_TYPE 2 //PCF8563 +#define RTC_TYPE 2 //PCF8563 #endif -#define MENU_BTN_MASK GPIO_SEL_26 -#define BACK_BTN_MASK GPIO_SEL_25 -#define DOWN_BTN_MASK GPIO_SEL_4 -#define ACC_INT_MASK GPIO_SEL_14 -#define BTN_PIN_MASK MENU_BTN_MASK | BACK_BTN_MASK | UP_BTN_MASK | DOWN_BTN_MASK +#define UP_BTN_MASK (BIT64(UP_BTN_PIN)) +#define MENU_BTN_MASK (BIT64(MENU_BTN_PIN)) +#define BACK_BTN_MASK (BIT64(BACK_BTN_PIN)) +#define DOWN_BTN_MASK (BIT64(DOWN_BTN_PIN)) +#define ACC_INT_MASK (BIT64(ACC_INT_1_PIN)) +#define BTN_PIN_MASK (MENU_BTN_MASK | BACK_BTN_MASK | UP_BTN_MASK | DOWN_BTN_MASK) #endif /* Pins_Arduino_h */ diff --git a/variants/waveshare_esp32_c6_zero/pins_arduino.h b/variants/waveshare_esp32_c6_zero/pins_arduino.h new file mode 100644 index 00000000000..4dd10b8d2db --- /dev/null +++ b/variants/waveshare_esp32_c6_zero/pins_arduino.h @@ -0,0 +1,105 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" +/* + Arduino Pin Definitions for Waveshare ESP32-C6-Zero + +----------------------------------------------------------------+ + | | | | # | USB | # | | | | + |:---:|:-------:|:--------:|:--:|:---:|:--:|:------:|:----:|:---:| + | | | 5V | 1 | TOP | 18 | GPIO16 | TX | D16 | + | | | GND | 2 | TOP | 17 | GPIO17 | RX | D17 | + | | | 3V3(OUT) | 3 | TOP | 16 | GPIO14 | SDA | D12 | + | D0 | A0 | GPIO0 | 4 | TOP | 15 | GPIO15 | SCL | D11 | + | D1 | A1 | GPIO1 | 5 | TOP | 14 | GPIO18 | SS | D10 | + | D2 | A2 | GPIO2 | 6 | TOP | 13 | GPIO19 | MOSI | D9 | + | D3 | A3 | GPIO3 | 7 | TOP | 12 | GPIO20 | MISO | D8 | + | D4 | A4 | GPIO4 | 8 | TOP | 11 | GPIO21 | SCK | D7 | + | D5 | A5 | GPIO5 | 9 | TOP | 10 | GPIO22 | | D6 | + +----------------------------------------------------------------+ + + +----------------------------------------------------------------+ + | | | | # | USB | # | | | | + |:---:|:-------:|:--------:|:--:|:---:|:--:|:------:|:----:|:---:| + | | | | | BOT | | | | | + | | | | | BOT | | | | | + | D21 | | GPIO13 | 19 | BOT | | | | | + | D20 | | GPIO12 | 20 | BOT | | | | | + | D19 | | GPIO23 | 21 | BOT | | | | | + | D18 | BOOT | GPIO9 | 22 | BOT | | | | | + | D13 | RGB_LED | GPIO8 | 23 | BOT | | | | | + | D15 | | GPIO7 | 24 | BOT | | | | | + | D14 | A6 | GPIO6 | 25 | BOT | | | | | + +----------------------------------------------------------------+ +*/ +// The built-in RGB LED is connected to this pin +static const uint8_t PIN_RGB_LED = 8; +#define PIN_RGB_LED PIN_RGB_LED // allow testing #ifdef PIN_RGB_LED + +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +// but also used in new Arduino API rgbLedWrite() +static const uint8_t RGB_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; +#define RGB_BUILTIN RGB_BUILTIN // allow testing #ifdef RGB_BUILTIN + +// Define default brightness for the built-in RGB LED +#define RGB_BRIGHTNESS 32 // default brightness level (0-255) + +// Define the color order for the built-in RGB LED +#define RGB_BUILTIN_LED_COLOR_ORDER LED_COLOR_ORDER_RGB // default WS2812B color order + +// Define the built-in as LED pin (RGB LED) to use with digitalWrite() +static const uint8_t LED_BUILTIN = RGB_BUILTIN; +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 16; +static const uint8_t RX = 17; + +static const uint8_t SDA = 14; +static const uint8_t SCL = 15; + +static const uint8_t SS = 18; +static const uint8_t MOSI = 19; +static const uint8_t MISO = 20; +static const uint8_t SCK = 21; + +static const uint8_t A0 = 0; +static const uint8_t A1 = 1; +static const uint8_t A2 = 2; +static const uint8_t A3 = 3; +static const uint8_t A4 = 4; +static const uint8_t A5 = 5; +static const uint8_t A6 = 6; + +static const uint8_t D0 = 0; +static const uint8_t D1 = 1; +static const uint8_t D2 = 2; +static const uint8_t D3 = 3; +static const uint8_t D4 = 4; +static const uint8_t D5 = 5; +static const uint8_t D6 = 22; +static const uint8_t D7 = 21; +static const uint8_t D8 = 20; +static const uint8_t D9 = 19; +static const uint8_t D10 = 18; +static const uint8_t D11 = 15; +static const uint8_t D12 = 14; +static const uint8_t D13 = 8; +static const uint8_t D14 = 6; +static const uint8_t D15 = 7; +static const uint8_t D16 = 16; +static const uint8_t D17 = 17; +static const uint8_t D18 = 9; +static const uint8_t D19 = 23; +static const uint8_t D20 = 12; +static const uint8_t D21 = 13; + +// LP I2C Pins are fixed on ESP32-C6 +#define WIRE1_PIN_DEFINED +static const uint8_t SDA1 = 6; +static const uint8_t SCL1 = 7; + +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define BUILTIN_RGB RGB_BUILTIN // backward compatibility + +#endif /* Pins_Arduino_h */ diff --git a/variants/waveshare_esp32_s3_lcd_169/pins_arduino.h b/variants/waveshare_esp32_s3_lcd_169/pins_arduino.h index 54663a6810a..5fafc59a02f 100644 --- a/variants/waveshare_esp32_s3_lcd_169/pins_arduino.h +++ b/variants/waveshare_esp32_s3_lcd_169/pins_arduino.h @@ -40,7 +40,8 @@ #define WS_SYS_EN 35 // Partial voltage measurement method -#define WS_BAT_ADC 1 +#define WS_BAT_ADC 1 +#define BAT_VOLT_PIN WS_BAT_ADC // UART0 pins static const uint8_t TX = 43; diff --git a/variants/waveshare_esp32_s3_touch_amoled_143/pins_arduino.h b/variants/waveshare_esp32_s3_touch_amoled_143/pins_arduino.h index ed6df1d3a2c..1c06e9c63cb 100644 --- a/variants/waveshare_esp32_s3_touch_amoled_143/pins_arduino.h +++ b/variants/waveshare_esp32_s3_touch_amoled_143/pins_arduino.h @@ -31,7 +31,8 @@ // RTC #define RTC_INT 15 // Partial voltage measurement method -#define BAT_ADC 4 +#define BAT_ADC 4 +#define BAT_VOLT_PIN BAT_ADC // Onboard QMI8658 IMU #define QMI_INT1 8 diff --git a/variants/waveshare_esp32_s3_touch_amoled_164/pins_arduino.h b/variants/waveshare_esp32_s3_touch_amoled_164/pins_arduino.h index ce17a49972a..47aabe97869 100644 --- a/variants/waveshare_esp32_s3_touch_amoled_164/pins_arduino.h +++ b/variants/waveshare_esp32_s3_touch_amoled_164/pins_arduino.h @@ -32,7 +32,8 @@ //key #define KEY_0 0 //ADC -#define BAT_ADC 4 +#define BAT_ADC 4 +#define BAT_VOLT_PIN BAT_ADC //SD_CARD #define SD_CS 38 diff --git a/variants/waveshare_esp32_s3_touch_amoled_191/pins_arduino.h b/variants/waveshare_esp32_s3_touch_amoled_191/pins_arduino.h index 7e882a7ef46..61454067521 100644 --- a/variants/waveshare_esp32_s3_touch_amoled_191/pins_arduino.h +++ b/variants/waveshare_esp32_s3_touch_amoled_191/pins_arduino.h @@ -29,7 +29,8 @@ #define TP_INT -1 // Partial voltage measurement method -#define BAT_ADC 1 +#define BAT_ADC 1 +#define BAT_VOLT_PIN BAT_ADC // Onboard QMI8658 IMU #define QMI_INT1 45 #define QMI_INT1 46 diff --git a/variants/waveshare_esp32_s3_touch_amoled_241/pins_arduino.h b/variants/waveshare_esp32_s3_touch_amoled_241/pins_arduino.h index cb6c5f40ac1..0b9ee51cfe6 100644 --- a/variants/waveshare_esp32_s3_touch_amoled_241/pins_arduino.h +++ b/variants/waveshare_esp32_s3_touch_amoled_241/pins_arduino.h @@ -42,7 +42,8 @@ #define QMI8658_INT1 -1 // Partial voltage measurement method -#define BAT_ADC 17 +#define BAT_ADC 17 +#define BAT_VOLT_PIN BAT_ADC // Def for I2C that shares the IMU I2C pins static const uint8_t SDA = 47; diff --git a/variants/waveshare_esp32_s3_touch_lcd_169/pins_arduino.h b/variants/waveshare_esp32_s3_touch_lcd_169/pins_arduino.h index 8d1562f4cd7..d512bda8cac 100644 --- a/variants/waveshare_esp32_s3_touch_lcd_169/pins_arduino.h +++ b/variants/waveshare_esp32_s3_touch_lcd_169/pins_arduino.h @@ -46,7 +46,8 @@ #define WS_SYS_EN 35 // Partial voltage measurement method -#define WS_BAT_ADC 1 +#define WS_BAT_ADC 1 +#define BAT_VOLT_PIN WS_BAT_ADC // UART0 pins static const uint8_t TX = 43; diff --git a/variants/wesp32/pins_arduino.h b/variants/wesp32/pins_arduino.h index ad1ee1d225e..7be7f87f3f5 100644 --- a/variants/wesp32/pins_arduino.h +++ b/variants/wesp32/pins_arduino.h @@ -35,7 +35,7 @@ static const uint8_t T9 = 32; #define ETH_PHY_POWER -1 #define ETH_PHY_MDC 16 #define ETH_PHY_MDIO 17 -#define ETH_PHY_TYPE ETH_PHY_LAN8720 +#define ETH_PHY_TYPE ETH_PHY_RTL8201 #define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN #endif /* Pins_Arduino_h */