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 af97de73644..76cac3d612a 100644 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ b/.github/ISSUE_TEMPLATE/Issue-report.yml @@ -40,9 +40,10 @@ 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 @@ -60,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/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/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 index 85e35c40e43..136b4dc044b 100755 --- a/.github/scripts/get_affected.py +++ b/.github/scripts/get_affected.py @@ -63,7 +63,7 @@ Build file patterns -------------------- - **build_files**: Core Arduino build system files (platform.txt, variants/**, etc.) -- **sketch_build_files**: Sketch-specific files (ci.json, *.csv in example directories) +- **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.) @@ -128,7 +128,7 @@ # 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.json", + "libraries/*/examples/**/ci.yml", "libraries/*/examples/**/*.csv", ] @@ -150,7 +150,7 @@ # 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.json", + "idf_component_examples/*/ci.yml", "idf_component_examples/*/*.csv", "idf_component_examples/*/sdkconfig*", "idf_component_examples/*/main/*", @@ -358,6 +358,59 @@ def build_qname_from_tag(tag: dict) -> str: 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: @@ -582,8 +635,10 @@ def build_dependencies_graph() -> None: if qnames: impl_files = set() for qn in qnames: - # For each qualified name, get the implementation files - impl_files |= ctags_defs_by_qname.get(qn, set()) + # 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'): @@ -735,6 +790,19 @@ def find_affected_sketches(changed_files: list[str]) -> None: 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 diff --git a/.github/scripts/on-push-idf.sh b/.github/scripts/on-push-idf.sh index 166bfe13eb1..66a61b2dff6 100644 --- a/.github/scripts/on-push-idf.sh +++ b/.github/scripts/on-push-idf.sh @@ -17,9 +17,9 @@ fi for example in $affected_examples; do example_path="$PWD/components/arduino-esp32/$example" - if [ -f "$example_path/ci.json" ]; then + 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_path/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 diff --git a/.github/scripts/on-release.sh b/.github/scripts/on-release.sh index e461e77b0c3..c4dadccc4f7 100755 --- a/.github/scripts/on-release.sh +++ b/.github/scripts/on-release.sh @@ -54,30 +54,6 @@ if [ -n "${VENDOR}" ]; then echo "Setting packager: $VENDOR" fi -function update_version { - set -e - set -o pipefail - - local tag=$1 - local major - local minor - local patch - - # Extract major, minor, and patch from the tag - # We need to make sure to remove the "v" prefix from the tag and any characters after the patch version - tag=$(echo "$tag" | sed 's/^v//g' | sed 's/-.*//g') - major=$(echo "$tag" | cut -d. -f1) - minor=$(echo "$tag" | cut -d. -f2) - patch=$(echo "$tag" | cut -d. -f3 | sed 's/[^0-9].*//') # Remove non-numeric suffixes like RC1, alpha, beta - - echo "Major: $major, Minor: $minor, Patch: $patch" - - "${SCRIPTS_DIR}/update-version.sh" "$major" "$minor" "$patch" - - set +e - set +o pipefail -} - function get_file_size { local file="$1" if [[ "$OSTYPE" == "darwin"* ]]; then @@ -230,7 +206,7 @@ LIBS_ZIP="$PACKAGE_NAME-libs.zip" LIBS_XZ="$PACKAGE_NAME-libs.tar.xz" echo "Updating version..." -if ! update_version "$RELEASE_TAG"; then +if ! "${SCRIPTS_DIR}/update-version.sh" "$RELEASE_TAG"; then echo "ERROR: update_version failed!" exit 1 fi @@ -415,6 +391,11 @@ 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 ..." 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/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 02990ca8914..6d1746285e1 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 @@ -155,8 +155,8 @@ function build_sketch { # build_sketch [ext esp32c3_opts=$(echo "$debug_level,$fqbn_append" | sed 's/^,*//;s/,*$//;s/,\{2,\}/,/g') 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') + esp32p4_opts=$(echo "PSRAM=enabled,USBMode=default,ChipVariant=postv3,$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,7 +244,7 @@ function build_sketch { # build_sketch [ext fi fi - # Install libraries from ci.json if they exist + # Install libraries from ci.yml if they exist install_libs -ai "$ide_path" -s "$sketchdir" install_result=$? if [ $install_result -ne 0 ]; then @@ -294,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}') @@ -337,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" \ @@ -394,9 +403,9 @@ function count_sketches { # count_sketches [target] [ignore-requirements] 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 @@ -637,38 +646,38 @@ function install_libs { # install_libs [-v] return 1 fi - if [ ! -f "$sketchdir/ci.json" ]; then - [ "$verbose" = true ] && echo "No ci.json found in $sketchdir, skipping library installation" + if [ ! -f "$sketchdir/ci.yml" ]; then + [ "$verbose" = true ] && echo "No ci.yml found in $sketchdir, skipping library installation" return 0 fi - if ! jq -e . "$sketchdir/ci.json" >/dev/null 2>&1; then - echo "ERROR: $sketchdir/ci.json is not valid JSON" >&2 + 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=$(jq -r '.libs | type' "$sketchdir/ci.json" 2>/dev/null) - if [ -z "$libs_type" ] || [ "$libs_type" = "null" ]; then - [ "$verbose" = true ] && echo "No libs field found in ci.json, skipping library installation" + 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" != "array" ]; then - echo "ERROR: libs field in ci.json must be an array, found: $libs_type" >&2 + 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=$(jq -r '.libs | length' "$sketchdir/ci.json" 2>/dev/null) + 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.json, skipping library installation" + [ "$verbose" = true ] && echo "libs array is empty in ci.yml, skipping library installation" return 0 fi - echo "Installing $libs_count libraries from $sketchdir/ci.json" + echo "Installing $libs_count libraries from $sketchdir/ci.yml" local needs_unsafe=false local original_unsafe_setting="" local libs - libs=$(jq -r '.libs[]? // empty' "$sketchdir/ci.json") + 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 @@ -749,7 +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.json file. + install_libs: Install libraries from ci.yml file. " cmd=$1 diff --git a/.github/scripts/tests_matrix.sh b/.github/scripts/tests_matrix.sh index 01cc122753c..6b4f8001fc3 100644 --- a/.github/scripts/tests_matrix.sh +++ b/.github/scripts/tests_matrix.sh @@ -1,29 +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 "[$hw_types]" > info/hw_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 26ec1a10d43..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 @@ -92,7 +92,6 @@ function run_test { 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") @@ -111,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 \"%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 -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}; 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 \"%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 -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}; 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,6 +144,8 @@ platform="hardware" chunk_run=0 options=0 erase=0 +wifi_ssid="" +wifi_password="" while [ -n "$1" ]; do case $1 in @@ -151,7 +160,6 @@ while [ -n "$1" ]; do platform="qemu" ;; -W ) - shift if [[ -z $WOKWI_CLI_TOKEN ]]; then echo "Wokwi CLI token is not set" exit 1 @@ -188,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 5dd26d9f40a..814cf24afd6 100755 --- a/.github/scripts/update-version.sh +++ b/.github/scripts/update-version.sh @@ -10,23 +10,26 @@ set -o pipefail # "[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 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/') @@ -35,38 +38,38 @@ if [ -z "$ESP_IDF_VERSION" ]; then 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$ESP_ARDUINO_VERSION" .github/ISSUE_TEMPLATE/Issue-report.yml; then +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$ESP_ARDUINO_VERSION/g" > __issue-report.yml && mv __issue-report.yml .github/ISSUE_TEMPLATE/Issue-report.yml - echo "Issue template updated with version v$ESP_ARDUINO_VERSION" + 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$ESP_ARDUINO_VERSION already exists in issue template, skipping update" + 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\"/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 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 | \ @@ -78,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/workflows/build_component.yml b/.github/workflows/build_component.yml index bc32f7a8999..f69532d021d 100644 --- a/.github/workflows/build_component.yml +++ b/.github/workflows/build_component.yml @@ -4,13 +4,13 @@ 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: false push: @@ -26,7 +26,6 @@ on: - "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" @@ -37,6 +36,7 @@ on: - "variants/esp32c3/**" - "variants/esp32c5/**" - "variants/esp32c6/**" + - "variants/esp32c61/**" - "variants/esp32h2/**" - "variants/esp32p4/**" - "variants/esp32s2/**" @@ -44,6 +44,7 @@ on: - "!*.md" - "!*.txt" - "!*.properties" + - "CMakeLists.txt" permissions: contents: read @@ -125,7 +126,7 @@ jobs: echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" ;; "release-v5.5") - echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c5,esp32c6,esp32h2,esp32p4" + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c5,esp32c6,esp32c61,esp32h2,esp32p4" ;; *) echo "" @@ -205,6 +206,14 @@ jobs: - 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: diff --git a/.github/workflows/build_py_tools.yml b/.github/workflows/build_py_tools.yml index e22d8df5eff..0b19e1fcf33 100644 --- a/.github/workflows/build_py_tools.yml +++ b/.github/workflows/build_py_tools.yml @@ -8,6 +8,7 @@ on: - "tools/espota.py" - "tools/gen_esp32part.py" - "tools/gen_insights_package.py" + - "tools/bin_signing.py" permissions: contents: write @@ -44,6 +45,7 @@ jobs: tools/espota.py tools/gen_esp32part.py tools/gen_insights_package.py + tools/bin_signing.py - name: List all changed files shell: bash @@ -108,7 +110,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install pyinstaller requests + pip install pyinstaller requests cryptography - name: Build with PyInstaller shell: bash @@ -119,16 +121,14 @@ jobs: - name: Sign binaries if: matrix.os == 'windows-latest' - env: - CERTIFICATE: ${{ secrets.CERTIFICATE }} - CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} - shell: pwsh - run: | - $data = Write-Output ${{ env.CHANGED_TOOLS }} - foreach ( $node in $data ) - { - ./.github/pytools/Sign-File.ps1 -Path ./${{ env.DISTPATH }}/$node.exe - } + uses: espressif/release-sign@33f3684091168d78b2cf6c8265bd9968c376254c # master + with: + path: ./${{ env.DISTPATH }} + azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} + azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} + azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} + azure-keyvault-uri: ${{ secrets.AZURE_KEYVAULT_URI }} + azure-keyvault-cert-name: ${{ secrets.AZURE_KEYVAULT_CERT_NAME }} - name: Test binaries shell: bash diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 0c54d24aaf9..01eb2b773dc 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -19,6 +19,7 @@ permissions: 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/push.yml b/.github/workflows/push.yml index 690993504c2..0dd76135dd9 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -29,11 +29,9 @@ 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-*" @@ -51,6 +49,8 @@ on: - "!*.md" - "!*.txt" - "!*.properties" + - "platform.txt" + - "programmers.txt" concurrency: group: build-${{github.event.pull_request.number || github.ref}} @@ -188,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 acba6c54e68..f8da07c1957 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,3 +36,45 @@ jobs: env: 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 8c46ef07661..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 @@ -54,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') }} @@ -84,22 +83,21 @@ jobs: strategy: matrix: type: ${{ fromJson(needs.gen-matrix.outputs.build-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 }} diff --git a/.github/workflows/tests_build.yml b/.github/workflows/tests_build.yml index bf5a33f538f..cd28cc9afbf 100644 --- a/.github/workflows/tests_build.yml +++ b/.github/workflows/tests_build.yml @@ -33,6 +33,7 @@ jobs: ~/.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 @@ -80,6 +81,7 @@ jobs: ~/.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 @@ -91,3 +93,4 @@ jobs: ~/.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_wokwi.yml b/.github/workflows/tests_hw_wokwi.yml index 14e7085eabb..e7f7d1e5f29 100644 --- a/.github/workflows/tests_hw_wokwi.yml +++ b/.github/workflows/tests_hw_wokwi.yml @@ -24,10 +24,12 @@ jobs: 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 }} - wokwi_types: ${{ steps.set-ref.outputs.wokwi_types }} 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 @@ -67,8 +69,13 @@ jobs: 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="" @@ -89,16 +96,28 @@ jobs: base=${{ github.ref }} fi - 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 [ -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" + 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:]:-") @@ -106,55 +125,74 @@ jobs: push_time="" fi - wokwi_types=$(cat artifacts/matrix_info/wokwi_types.txt | tr -cd "[:alpha:],[]'") - hw_types=$(cat artifacts/matrix_info/hw_types.txt | tr -cd "[:alpha:],[]'") - targets=$(cat artifacts/matrix_info/targets.txt | tr -cd "[:alnum:],[]'") + 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 "targets = $targets" - echo "wokwi_types = $wokwi_types" + 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" - 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 + 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 <> 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 "wokwi_types=$wokwi_types" >> $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 @@ -256,8 +294,9 @@ jobs: echo "enabled=$enabled" >> $GITHUB_OUTPUT - - name: Wait for GitLab sync + - 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: | @@ -299,6 +338,16 @@ jobs: 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 @@ -312,7 +361,14 @@ jobs: download_artifacts: 'true' download_artifacts_on_failure: 'true' download_path: './gitlab-artifacts' - variables: '{"TEST_TYPES":"${{ needs.get-artifacts.outputs.hw_types }}","TEST_CHIPS":"${{ needs.get-artifacts.outputs.targets }}","PIPELINE_ID":"${{ env.id }}","BINARIES_RUN_ID":"${{ github.event.workflow_run.id }}","GITHUB_REPOSITORY":"${{ github.repository }}"}' + 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' }} @@ -388,9 +444,10 @@ jobs: wokwi-test: name: Wokwi ${{ matrix.chip }} ${{ matrix.type }} tests if: | - github.event.workflow_run.conclusion == 'success' || + (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' || - github.event.workflow_run.conclusion == 'timed_out' + github.event.workflow_run.conclusion == 'timed_out') && + needs.get-artifacts.outputs.wokwi_tests_enabled == 'true' runs-on: ubuntu-latest needs: get-artifacts env: @@ -402,7 +459,7 @@ jobs: fail-fast: false matrix: type: ${{ fromJson(needs.get-artifacts.outputs.wokwi_types) }} - chip: ${{ fromJson(needs.get-artifacts.outputs.targets) }} + chip: ${{ fromJson(needs.get-artifacts.outputs.wokwi_targets) }} steps: - name: Report pending uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -486,8 +543,11 @@ jobs: 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 + 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 diff --git a/.github/workflows/tests_results.yml b/.github/workflows/tests_results.yml index 525b303e486..7b638a0e972 100644 --- a/.github/workflows/tests_results.yml +++ b/.github/workflows/tests_results.yml @@ -21,6 +21,15 @@ jobs: 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 @@ -31,36 +40,34 @@ jobs: - name: Get original info id: get-info run: | - echo "Artifacts:" - ls -laR ./artifacts + # Inputs in workflow_info.json are already sanitized and safe to use - 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) + 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) - # 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:]') + 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 @@ -68,6 +75,15 @@ jobs: 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" @@ -107,13 +123,41 @@ jobs: run_id: ${{ github.event.workflow_run.id }} path: ./artifacts + - 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: 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: | + 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: ${{ needs.get-artifacts.outputs.original_sha }} event_file: ./artifacts/parent-artifacts/event_file/event.json event_name: ${{ needs.get-artifacts.outputs.original_event }} - files: ./artifacts/**/*.xml + files: | + ./artifacts/**/*.xml + ./test_errors/**/*.xml action_fail: true action_fail_on_inconclusive: true compare_to_earlier_commit: false @@ -128,16 +172,6 @@ jobs: overwrite: true path: ./unity_results.json - - name: Fail if tests failed - if: | - 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' - run: exit 1 - - name: Clean up caches if: always() uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 @@ -206,22 +240,36 @@ jobs: (needs.get-artifacts.outputs.original_event == 'schedule' || needs.get-artifacts.outputs.original_event == 'workflow_dispatch') env: - REPORT_FILE: ./runtime-test-results/RUNTIME_TEST_RESULTS.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: ${{ needs.get-artifacts.outputs.original_run_id }} - 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' + 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-test-results/unity_results.json touch $REPORT_FILE - python3 ./runtime-test-results/table_generator.py ./runtime-test-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 @@ -250,7 +298,7 @@ jobs: 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 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/.gitlab/scripts/gen_hw_jobs.py b/.gitlab/scripts/gen_hw_jobs.py index 804e245c18f..5dd77cf56af 100644 --- a/.gitlab/scripts/gen_hw_jobs.py +++ b/.gitlab/scripts/gen_hw_jobs.py @@ -8,6 +8,10 @@ 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 @@ -32,12 +36,12 @@ def str_representer(dumper, data): return dumper.represent_scalar("tag:yaml.org,2002:str", data, style=style) -def read_json(p: Path): +def read_yaml(p: Path): try: with p.open("r", encoding="utf-8") as f: - return json.load(f) + return yaml.safe_load(f) or {} except Exception as e: - sys.stderr.write(f"[WARN] Failed to parse JSON file '{p}': {e}\n") + sys.stderr.write(f"[WARN] Failed to parse YAML file '{p}': {e}\n") sys.stderr.write(traceback.format_exc() + "\n") return {} @@ -46,7 +50,7 @@ def find_tests() -> list[Path]: tests = [] if not TESTS_ROOT.exists(): return tests - for ci in TESTS_ROOT.rglob("ci.json"): + for ci in TESTS_ROOT.rglob("ci.yml"): if ci.is_file(): tests.append(ci) return tests @@ -79,12 +83,11 @@ def find_sketch_test_dirs(types_filter: list[str]) -> list[tuple[str, Path]]: def load_tags_for_test(ci_json: dict, chip: str) -> set[str]: tags = set() # Global tags - for key in "tags": - v = ci_json.get(key) - if isinstance(v, list): - for e in v: - if isinstance(e, str) and e.strip(): - tags.add(e.strip()) + 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): @@ -184,6 +187,147 @@ def parse_list_arg(s: str) -> list[str]: 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") @@ -209,12 +353,12 @@ def main(): # 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.json files under 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.json" - ci = read_json(ci_path) if ci_path.exists() else {} + 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: @@ -249,26 +393,89 @@ def main(): # 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" - 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)) + + # 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 = {} 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 5702954e9e9..0f4bda9bcce 100644 --- a/.gitlab/workflows/common.yml +++ b/.gitlab/workflows/common.yml @@ -13,7 +13,7 @@ stages: variables: ESP_IDF_VERSION: "5.5" - ESP_ARDUINO_VERSION: "3.3.2" + ESP_ARDUINO_VERSION: "3.3.4" ############# # `default` # diff --git a/.gitlab/workflows/hardware_tests_dynamic.yml b/.gitlab/workflows/hardware_tests_dynamic.yml index 2c137f092ab..9b88fabab34 100644 --- a/.gitlab/workflows/hardware_tests_dynamic.yml +++ b/.gitlab/workflows/hardware_tests_dynamic.yml @@ -3,7 +3,7 @@ ############################### # This parent workflow generates a dynamic child pipeline with jobs grouped -# by SOC + runner tags derived from tests' ci.json, then triggers it and waits. +# by SOC + runner tags derived from tests' ci.yml, then triggers it and waits. generate-hw-tests: stage: generate @@ -16,9 +16,7 @@ generate-hw-tests: TEST_TYPES: $TEST_TYPES TEST_CHIPS: $TEST_CHIPS before_script: - - pip install PyYAML - - apt-get update - - apt-get install -y jq unzip curl + - bash .gitlab/scripts/install_dependencies.sh script: - mkdir -p ~/.arduino/tests - | diff --git a/.gitlab/workflows/hw_test_template.yml b/.gitlab/workflows/hw_test_template.yml index 1b09c2cb7eb..f0fddebcd0e 100644 --- a/.gitlab/workflows/hw_test_template.yml +++ b/.gitlab/workflows/hw_test_template.yml @@ -12,13 +12,13 @@ include: hw-test-template: stage: test image: python:3.12-bookworm + timeout: 5h rules: - when: on_success variables: RUNNER_SCRIPT_TIMEOUT: 4h - RUNNER_AFTER_SCRIPT_TIMEOUT: 2h DEBIAN_FRONTEND: "noninteractive" TEST_TYPE: $TEST_TYPE TEST_CHIP: $TEST_CHIP @@ -33,8 +33,7 @@ hw-test-template: - echo "Running hardware tests for chip:$TEST_CHIP type:$TEST_TYPE" - echo "Pipeline ID:$PIPELINE_ID" - echo "Running hardware tests for chip:$TEST_CHIP" - - apt-get update - - apt-get install -y jq unzip curl + - bash .gitlab/scripts/install_dependencies.sh - rm -rf ~/.arduino/tests - mkdir -p ~/.arduino/tests/$TEST_CHIP - echo Fetching binaries for $TEST_CHIP $TEST_TYPE @@ -51,7 +50,7 @@ hw-test-template: [ -z "$d" ] && continue; sketch=$(basename "$d"); echo Running $sketch in $d; - bash .github/scripts/tests_run.sh -t $TEST_CHIP -s $sketch -e || rc=$?; + 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: diff --git a/CMakeLists.txt b/CMakeLists.txt index d9b295dfa70..8f142cd4b46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,6 +90,7 @@ set(ARDUINO_ALL_LIBRARIES ESP_I2S ESP_NOW ESP_SR + ESP_HostedOTA ESPmDNS Ethernet FFat @@ -146,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) @@ -187,12 +191,18 @@ 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/MatterEndpoints/MatterWindowCovering.cpp libraries/Matter/src/Matter.cpp libraries/Matter/src/MatterEndPoint.cpp) @@ -229,7 +239,8 @@ set(ARDUINO_LIBRARY_Ticker_SRCS libraries/Ticker/src/Ticker.cpp) set(ARDUINO_LIBRARY_Update_SRCS libraries/Update/src/Updater.cpp - libraries/Update/src/HttpsOTAUpdate.cpp) + libraries/Update/src/HttpsOTAUpdate.cpp + libraries/Update/src/Updater_Signing.cpp) set(ARDUINO_LIBRARY_USB_SRCS libraries/USB/src/USBHID.cpp diff --git a/Kconfig.projbuild b/Kconfig.projbuild index 705d0e66d5a..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" diff --git a/README.md b/README.md index 51ab98e0af1..d8a08b00689 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Arduino core for the ESP32, ESP32-C3, ESP32-C5, ESP32-C6, ESP32-H2, ESP32-P4, ESP32-S2 and ESP32-S3. +# Arduino core for the ESP32 family of SoCs [![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) @@ -76,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 3ed6e80f0b3..a2c988e93ee 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 @@ -107,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 @@ -249,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) @@ -398,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 @@ -414,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 @@ -486,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) @@ -512,12 +521,6 @@ 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 @@ -660,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) @@ -858,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) @@ -983,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 @@ -1144,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) @@ -1321,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) @@ -1529,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) @@ -1712,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) @@ -1892,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) @@ -2050,7 +2223,7 @@ 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.rainmaker=RainMaker 4MB @@ -2350,7 +2523,7 @@ 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) @@ -2601,7 +2774,7 @@ 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.custom=Custom @@ -2701,7 +2874,7 @@ 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 @@ -2819,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) @@ -2922,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) @@ -3154,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) @@ -3280,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) @@ -3438,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) @@ -3596,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) @@ -4089,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 @@ -4231,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 @@ -4559,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 @@ -5572,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 @@ -5924,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) @@ -6350,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 @@ -7016,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 @@ -7334,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 @@ -7464,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 @@ -7590,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 @@ -7716,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 @@ -8190,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) @@ -8415,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 @@ -8564,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) @@ -8732,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) @@ -8974,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) @@ -9154,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) @@ -9337,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 @@ -9479,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) @@ -9699,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) @@ -10847,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 @@ -11018,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 @@ -11755,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) @@ -12241,7 +12414,7 @@ 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.rainmaker=RainMaker 4MB @@ -12387,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 @@ -12538,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) @@ -12791,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) @@ -12933,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 @@ -13158,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 @@ -13345,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 @@ -13750,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 @@ -14136,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 @@ -14319,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 @@ -14460,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 @@ -14757,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 @@ -14940,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 @@ -15123,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 @@ -15332,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 @@ -15737,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 @@ -15955,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 @@ -16115,7 +16288,7 @@ 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.rainmaker=RainMaker 4MB @@ -16388,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 @@ -16569,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 @@ -16965,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 @@ -17498,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 @@ -17851,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 @@ -17985,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 @@ -18119,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 @@ -18547,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) @@ -19262,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) @@ -19405,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) @@ -19681,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) @@ -19876,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) @@ -20131,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) @@ -20289,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) @@ -20462,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) @@ -20645,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) @@ -20820,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) @@ -21066,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) @@ -21239,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) @@ -21413,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) @@ -21587,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) @@ -21754,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) @@ -21917,7 +22090,7 @@ 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.rainmaker=RainMaker 4MB @@ -22066,7 +22239,7 @@ 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.rainmaker=RainMaker 4MB @@ -22224,7 +22397,7 @@ 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.rainmaker=RainMaker 4MB @@ -22375,7 +22548,7 @@ 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.rainmaker=RainMaker 4MB @@ -22619,7 +22792,7 @@ 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.rainmaker=RainMaker 4MB @@ -22839,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) @@ -23034,7 +23207,7 @@ 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/190KB SPIFFS) +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) @@ -23168,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 @@ -23316,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 @@ -23556,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) @@ -23697,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 @@ -23847,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) @@ -24010,7 +24183,7 @@ 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.rainmaker=RainMaker 4MB @@ -24158,7 +24331,7 @@ 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.rainmaker=RainMaker 4MB @@ -24324,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 @@ -24553,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) @@ -24790,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) @@ -25030,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) @@ -25267,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) @@ -25504,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) @@ -28704,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) @@ -28908,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 @@ -29329,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) @@ -30193,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) @@ -30418,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) @@ -30906,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) @@ -31081,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) @@ -31228,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) @@ -31683,7 +31856,7 @@ 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 @@ -31960,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) @@ -32192,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) @@ -32663,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 @@ -32780,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 @@ -32890,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 @@ -33402,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) @@ -33580,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) @@ -33834,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 @@ -33981,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) @@ -34244,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) @@ -34413,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) @@ -34603,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) @@ -34772,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) @@ -35084,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) @@ -35240,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) @@ -35347,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) @@ -35520,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) @@ -35648,7 +35821,7 @@ 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 @@ -35867,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) @@ -36003,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 @@ -36216,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) @@ -36389,7 +36562,7 @@ 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/190KB SPIFFS) +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 @@ -37070,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 @@ -37303,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 @@ -37909,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) @@ -38151,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 @@ -38307,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 @@ -38780,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 @@ -38890,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) @@ -39030,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) @@ -39181,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) @@ -39412,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) @@ -39782,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) @@ -40008,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) @@ -40420,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 @@ -40609,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 @@ -40798,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 @@ -40930,7 +41103,7 @@ 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.rainmaker=RainMaker 4MB @@ -41036,7 +41209,7 @@ 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.rainmaker=RainMaker 4MB @@ -41165,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 @@ -41376,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 @@ -41559,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 @@ -41890,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 @@ -42213,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= @@ -42358,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 @@ -42546,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 @@ -42808,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 @@ -42936,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 @@ -42977,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 @@ -43170,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 @@ -43363,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 @@ -43559,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 @@ -43755,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 @@ -43900,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) @@ -43977,6 +44315,333 @@ waveshare_esp32s3_touch_lcd_128.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +waveshare_esp32_c3_zero.name=Waveshare ESP32-C3-Zero + +waveshare_esp32_c3_zero.bootloader.tool=esptool_py +waveshare_esp32_c3_zero.bootloader.tool.default=esptool_py + +waveshare_esp32_c3_zero.upload.tool=esptool_py +waveshare_esp32_c3_zero.upload.tool.default=esptool_py +waveshare_esp32_c3_zero.upload.tool.network=esp_ota + +waveshare_esp32_c3_zero.upload.maximum_size=1310720 +waveshare_esp32_c3_zero.upload.maximum_data_size=327680 +waveshare_esp32_c3_zero.upload.flags= +waveshare_esp32_c3_zero.upload.extra_flags= +waveshare_esp32_c3_zero.upload.use_1200bps_touch=false +waveshare_esp32_c3_zero.upload.wait_for_upload_port=false + +waveshare_esp32_c3_zero.serial.disableDTR=false +waveshare_esp32_c3_zero.serial.disableRTS=false + +waveshare_esp32_c3_zero.build.tarch=riscv32 +waveshare_esp32_c3_zero.build.target=esp +waveshare_esp32_c3_zero.build.mcu=esp32c3 +waveshare_esp32_c3_zero.build.core=esp32 +waveshare_esp32_c3_zero.build.variant=waveshare_esp32_c3_zero +waveshare_esp32_c3_zero.build.board=WAVESHARE_ESP32_C3_ZERO +waveshare_esp32_c3_zero.build.bootloader_addr=0x0 + +waveshare_esp32_c3_zero.build.cdc_on_boot=1 +waveshare_esp32_c3_zero.build.f_cpu=160000000L +waveshare_esp32_c3_zero.build.flash_size=4MB +waveshare_esp32_c3_zero.build.flash_freq=80m +waveshare_esp32_c3_zero.build.flash_mode=qio +waveshare_esp32_c3_zero.build.boot=qio +waveshare_esp32_c3_zero.build.partitions=default +waveshare_esp32_c3_zero.build.defines= + +## IDE 2.0 Seems to not update the value +waveshare_esp32_c3_zero.menu.JTAGAdapter.default=Disabled +waveshare_esp32_c3_zero.menu.JTAGAdapter.default.build.copy_jtag_files=0 +waveshare_esp32_c3_zero.menu.JTAGAdapter.builtin=Integrated USB JTAG +waveshare_esp32_c3_zero.menu.JTAGAdapter.builtin.build.openocdscript=esp32c3-builtin.cfg +waveshare_esp32_c3_zero.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +waveshare_esp32_c3_zero.menu.JTAGAdapter.external=FTDI Adapter +waveshare_esp32_c3_zero.menu.JTAGAdapter.external.build.openocdscript=esp32c3-ftdi.cfg +waveshare_esp32_c3_zero.menu.JTAGAdapter.external.build.copy_jtag_files=1 +waveshare_esp32_c3_zero.menu.JTAGAdapter.bridge=ESP USB Bridge +waveshare_esp32_c3_zero.menu.JTAGAdapter.bridge.build.openocdscript=esp32c3-bridge.cfg +waveshare_esp32_c3_zero.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +waveshare_esp32_c3_zero.menu.CDCOnBoot.default=Enabled +waveshare_esp32_c3_zero.menu.CDCOnBoot.default.build.cdc_on_boot=1 +waveshare_esp32_c3_zero.menu.CDCOnBoot.cdc=Disabled +waveshare_esp32_c3_zero.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +waveshare_esp32_c3_zero.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.default.build.partitions=default +waveshare_esp32_c3_zero.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +waveshare_esp32_c3_zero.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.minimal.build.partitions=minimal +waveshare_esp32_c3_zero.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +waveshare_esp32_c3_zero.menu.PartitionScheme.no_fs.build.partitions=no_fs +waveshare_esp32_c3_zero.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +waveshare_esp32_c3_zero.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.no_ota.build.partitions=no_ota +waveshare_esp32_c3_zero.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +waveshare_esp32_c3_zero.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +waveshare_esp32_c3_zero.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.huge_app.build.partitions=huge_app +waveshare_esp32_c3_zero.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +waveshare_esp32_c3_zero.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/128KB SPIFFS) +waveshare_esp32_c3_zero.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +waveshare_esp32_c3_zero.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker=RainMaker 4MB +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +waveshare_esp32_c3_zero.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +waveshare_esp32_c3_zero.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +waveshare_esp32_c3_zero.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +waveshare_esp32_c3_zero.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +waveshare_esp32_c3_zero.menu.PartitionScheme.custom=Custom +waveshare_esp32_c3_zero.menu.PartitionScheme.custom.build.partitions= +waveshare_esp32_c3_zero.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +waveshare_esp32_c3_zero.menu.CPUFreq.160=160MHz (WiFi) +waveshare_esp32_c3_zero.menu.CPUFreq.160.build.f_cpu=160000000L +waveshare_esp32_c3_zero.menu.CPUFreq.80=80MHz (WiFi) +waveshare_esp32_c3_zero.menu.CPUFreq.80.build.f_cpu=80000000L +waveshare_esp32_c3_zero.menu.CPUFreq.40=40MHz +waveshare_esp32_c3_zero.menu.CPUFreq.40.build.f_cpu=40000000L +waveshare_esp32_c3_zero.menu.CPUFreq.20=20MHz +waveshare_esp32_c3_zero.menu.CPUFreq.20.build.f_cpu=20000000L +waveshare_esp32_c3_zero.menu.CPUFreq.10=10MHz +waveshare_esp32_c3_zero.menu.CPUFreq.10.build.f_cpu=10000000L + +waveshare_esp32_c3_zero.menu.FlashMode.qio=QIO +waveshare_esp32_c3_zero.menu.FlashMode.qio.build.flash_mode=dio +waveshare_esp32_c3_zero.menu.FlashMode.qio.build.boot=qio +waveshare_esp32_c3_zero.menu.FlashMode.dio=DIO +waveshare_esp32_c3_zero.menu.FlashMode.dio.build.flash_mode=dio +waveshare_esp32_c3_zero.menu.FlashMode.dio.build.boot=dio + +waveshare_esp32_c3_zero.menu.FlashFreq.80=80MHz +waveshare_esp32_c3_zero.menu.FlashFreq.80.build.flash_freq=80m +waveshare_esp32_c3_zero.menu.FlashFreq.40=40MHz +waveshare_esp32_c3_zero.menu.FlashFreq.40.build.flash_freq=40m + +waveshare_esp32_c3_zero.menu.FlashSize.4M=4MB (32Mb) +waveshare_esp32_c3_zero.menu.FlashSize.4M.build.flash_size=4MB + +waveshare_esp32_c3_zero.menu.UploadSpeed.921600=921600 +waveshare_esp32_c3_zero.menu.UploadSpeed.921600.upload.speed=921600 +waveshare_esp32_c3_zero.menu.UploadSpeed.115200=115200 +waveshare_esp32_c3_zero.menu.UploadSpeed.115200.upload.speed=115200 +waveshare_esp32_c3_zero.menu.UploadSpeed.256000.windows=256000 +waveshare_esp32_c3_zero.menu.UploadSpeed.256000.upload.speed=256000 +waveshare_esp32_c3_zero.menu.UploadSpeed.230400.windows.upload.speed=256000 +waveshare_esp32_c3_zero.menu.UploadSpeed.230400=230400 +waveshare_esp32_c3_zero.menu.UploadSpeed.230400.upload.speed=230400 +waveshare_esp32_c3_zero.menu.UploadSpeed.460800.linux=460800 +waveshare_esp32_c3_zero.menu.UploadSpeed.460800.macosx=460800 +waveshare_esp32_c3_zero.menu.UploadSpeed.460800.upload.speed=460800 +waveshare_esp32_c3_zero.menu.UploadSpeed.512000.windows=512000 +waveshare_esp32_c3_zero.menu.UploadSpeed.512000.upload.speed=512000 + +waveshare_esp32_c3_zero.menu.DebugLevel.none=None +waveshare_esp32_c3_zero.menu.DebugLevel.none.build.code_debug=0 +waveshare_esp32_c3_zero.menu.DebugLevel.error=Error +waveshare_esp32_c3_zero.menu.DebugLevel.error.build.code_debug=1 +waveshare_esp32_c3_zero.menu.DebugLevel.warn=Warn +waveshare_esp32_c3_zero.menu.DebugLevel.warn.build.code_debug=2 +waveshare_esp32_c3_zero.menu.DebugLevel.info=Info +waveshare_esp32_c3_zero.menu.DebugLevel.info.build.code_debug=3 +waveshare_esp32_c3_zero.menu.DebugLevel.debug=Debug +waveshare_esp32_c3_zero.menu.DebugLevel.debug.build.code_debug=4 +waveshare_esp32_c3_zero.menu.DebugLevel.verbose=Verbose +waveshare_esp32_c3_zero.menu.DebugLevel.verbose.build.code_debug=5 + +waveshare_esp32_c3_zero.menu.EraseFlash.none=Disabled +waveshare_esp32_c3_zero.menu.EraseFlash.none.upload.erase_cmd= +waveshare_esp32_c3_zero.menu.EraseFlash.all=Enabled +waveshare_esp32_c3_zero.menu.EraseFlash.all.upload.erase_cmd=-e + +waveshare_esp32_c3_zero.menu.ZigbeeMode.default=Disabled +waveshare_esp32_c3_zero.menu.ZigbeeMode.default.build.zigbee_mode= +waveshare_esp32_c3_zero.menu.ZigbeeMode.default.build.zigbee_libs= +waveshare_esp32_c3_zero.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +waveshare_esp32_c3_zero.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +waveshare_esp32_c3_zero.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +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=Enabled +waveshare_esp32_c6_zero.menu.CDCOnBoot.default.build.cdc_on_boot=1 +waveshare_esp32_c6_zero.menu.CDCOnBoot.cdc=Disabled +waveshare_esp32_c6_zero.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +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 @@ -44257,7 +44922,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 @@ -44566,7 +45231,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 @@ -45041,7 +45706,7 @@ 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.rainmaker=RainMaker 4MB @@ -45188,7 +45853,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 @@ -45416,7 +46081,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 @@ -45617,7 +46282,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 @@ -45813,7 +46478,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 @@ -46014,7 +46679,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 @@ -46210,7 +46875,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 @@ -46406,7 +47071,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 @@ -46602,7 +47267,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 @@ -46827,7 +47492,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) @@ -46987,7 +47652,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 @@ -47157,7 +47822,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 @@ -47404,7 +48069,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) @@ -47641,7 +48306,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) @@ -47878,7 +48543,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) @@ -48115,7 +48780,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) @@ -48348,7 +49013,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) @@ -48585,7 +49250,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) @@ -48822,7 +49487,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) @@ -49062,7 +49727,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) @@ -49275,7 +49940,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 @@ -49472,7 +50137,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 @@ -49669,7 +50334,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 @@ -49804,7 +50469,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 @@ -49955,7 +50620,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 @@ -50310,7 +50975,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) @@ -50520,7 +51185,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) @@ -50739,7 +51404,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 @@ -50949,7 +51614,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) @@ -51472,7 +52137,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) @@ -52130,7 +52795,7 @@ fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat.upload.maximum_size=10 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/190KB SPIFFS) +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 @@ -52325,7 +52990,7 @@ 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/190KB SPIFFS) +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 @@ -52562,6 +53227,154 @@ esp32p4_4ds_mipi.build.defines=-DBOARD_HAS_PSRAM -D{build.board} -D{build.Displa ############################################################## +esp32p4_4ds_mipi_round.name=4D Systems ESP32-P4 Round MIPI Displays + +esp32p4_4ds_mipi_round.bootloader.tool=esptool_py +esp32p4_4ds_mipi_round.bootloader.tool.default=esptool_py + +esp32p4_4ds_mipi_round.upload.tool=esptool_py +esp32p4_4ds_mipi_round.upload.tool.default=esptool_py +esp32p4_4ds_mipi_round.upload.tool.network=esp_ota + +esp32p4_4ds_mipi_round.upload.maximum_size=1310720 +esp32p4_4ds_mipi_round.upload.maximum_data_size=327680 +esp32p4_4ds_mipi_round.upload.flags= +esp32p4_4ds_mipi_round.upload.extra_flags= +esp32p4_4ds_mipi_round.upload.use_1200bps_touch=false +esp32p4_4ds_mipi_round.upload.wait_for_upload_port=false + +esp32p4_4ds_mipi_round.serial.disableDTR=false +esp32p4_4ds_mipi_round.serial.disableRTS=false + +esp32p4_4ds_mipi_round.build.tarch=riscv32 +esp32p4_4ds_mipi_round.build.target=esp +esp32p4_4ds_mipi_round.build.mcu=esp32p4 +esp32p4_4ds_mipi_round.build.core=esp32 +esp32p4_4ds_mipi_round.build.variant=esp32p4_4ds_mipi_round +esp32p4_4ds_mipi_round.build.board=ESP32P4_4DS_MIPI_ROUND +esp32p4_4ds_mipi_round.build.bootloader_addr=0x2000 + +esp32p4_4ds_mipi_round.build.usb_mode=0 +esp32p4_4ds_mipi_round.build.cdc_on_boot=0 +esp32p4_4ds_mipi_round.build.msc_on_boot=0 +esp32p4_4ds_mipi_round.build.dfu_on_boot=0 +esp32p4_4ds_mipi_round.build.f_cpu=360000000L +esp32p4_4ds_mipi_round.build.flash_size=32MB +esp32p4_4ds_mipi_round.build.flash_freq=80m +esp32p4_4ds_mipi_round.build.img_freq=80m +esp32p4_4ds_mipi_round.build.flash_mode=qio +esp32p4_4ds_mipi_round.build.boot=qio +esp32p4_4ds_mipi_round.build.partitions=app5M_fat24M_32MB + +esp32p4_4ds_mipi_round.menu.JTAGAdapter.default=Disabled +esp32p4_4ds_mipi_round.menu.JTAGAdapter.default.build.copy_jtag_files=0 +esp32p4_4ds_mipi_round.menu.JTAGAdapter.builtin=Integrated USB JTAG +esp32p4_4ds_mipi_round.menu.JTAGAdapter.builtin.build.openocdscript=esp32p4-builtin.cfg +esp32p4_4ds_mipi_round.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +esp32p4_4ds_mipi_round.menu.JTAGAdapter.external=FTDI Adapter +esp32p4_4ds_mipi_round.menu.JTAGAdapter.external.build.openocdscript=esp32p4-ftdi.cfg +esp32p4_4ds_mipi_round.menu.JTAGAdapter.external.build.copy_jtag_files=1 +esp32p4_4ds_mipi_round.menu.JTAGAdapter.bridge=ESP USB Bridge +esp32p4_4ds_mipi_round.menu.JTAGAdapter.bridge.build.openocdscript=esp32p4-bridge.cfg +esp32p4_4ds_mipi_round.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +esp32p4_4ds_mipi_round.menu.USBMode.default=USB-OTG (TinyUSB) +esp32p4_4ds_mipi_round.menu.USBMode.default.build.usb_mode=0 +esp32p4_4ds_mipi_round.menu.USBMode.hwcdc=Hardware CDC and JTAG +esp32p4_4ds_mipi_round.menu.USBMode.hwcdc.build.usb_mode=1 + +esp32p4_4ds_mipi_round.menu.CDCOnBoot.default=Disabled +esp32p4_4ds_mipi_round.menu.CDCOnBoot.default.build.cdc_on_boot=0 +esp32p4_4ds_mipi_round.menu.CDCOnBoot.cdc=Enabled +esp32p4_4ds_mipi_round.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +esp32p4_4ds_mipi_round.menu.MSCOnBoot.default=Disabled +esp32p4_4ds_mipi_round.menu.MSCOnBoot.default.build.msc_on_boot=0 +esp32p4_4ds_mipi_round.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi_round.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +esp32p4_4ds_mipi_round.menu.DFUOnBoot.default=Disabled +esp32p4_4ds_mipi_round.menu.DFUOnBoot.default.build.dfu_on_boot=0 +esp32p4_4ds_mipi_round.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi_round.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +esp32p4_4ds_mipi_round.menu.UploadMode.default=UART0 / Hardware CDC +esp32p4_4ds_mipi_round.menu.UploadMode.default.upload.use_1200bps_touch=false +esp32p4_4ds_mipi_round.menu.UploadMode.default.upload.wait_for_upload_port=false +esp32p4_4ds_mipi_round.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +esp32p4_4ds_mipi_round.menu.UploadMode.cdc.upload.use_1200bps_touch=true +esp32p4_4ds_mipi_round.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +esp32p4_4ds_mipi_round.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi_round.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +esp32p4_4ds_mipi_round.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +esp32p4_4ds_mipi_round.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_round.menu.CPUFreq.360=360MHz +esp32p4_4ds_mipi_round.menu.CPUFreq.360.build.f_cpu=360000000L +esp32p4_4ds_mipi_round.menu.CPUFreq.40=40MHz +esp32p4_4ds_mipi_round.menu.CPUFreq.40.build.f_cpu=40000000L + +esp32p4_4ds_mipi_round.menu.UploadSpeed.921600=921600 +esp32p4_4ds_mipi_round.menu.UploadSpeed.921600.upload.speed=921600 +esp32p4_4ds_mipi_round.menu.UploadSpeed.115200=115200 +esp32p4_4ds_mipi_round.menu.UploadSpeed.115200.upload.speed=115200 +esp32p4_4ds_mipi_round.menu.UploadSpeed.256000.windows=256000 +esp32p4_4ds_mipi_round.menu.UploadSpeed.256000.upload.speed=256000 +esp32p4_4ds_mipi_round.menu.UploadSpeed.230400.windows.upload.speed=256000 +esp32p4_4ds_mipi_round.menu.UploadSpeed.230400=230400 +esp32p4_4ds_mipi_round.menu.UploadSpeed.230400.upload.speed=230400 +esp32p4_4ds_mipi_round.menu.UploadSpeed.460800.linux=460800 +esp32p4_4ds_mipi_round.menu.UploadSpeed.460800.macosx=460800 +esp32p4_4ds_mipi_round.menu.UploadSpeed.460800.upload.speed=460800 +esp32p4_4ds_mipi_round.menu.UploadSpeed.512000.windows=512000 +esp32p4_4ds_mipi_round.menu.UploadSpeed.512000.upload.speed=512000 + +esp32p4_4ds_mipi_round.menu.DebugLevel.none=None +esp32p4_4ds_mipi_round.menu.DebugLevel.none.build.code_debug=0 +esp32p4_4ds_mipi_round.menu.DebugLevel.error=Error +esp32p4_4ds_mipi_round.menu.DebugLevel.error.build.code_debug=1 +esp32p4_4ds_mipi_round.menu.DebugLevel.warn=Warn +esp32p4_4ds_mipi_round.menu.DebugLevel.warn.build.code_debug=2 +esp32p4_4ds_mipi_round.menu.DebugLevel.info=Info +esp32p4_4ds_mipi_round.menu.DebugLevel.info.build.code_debug=3 +esp32p4_4ds_mipi_round.menu.DebugLevel.debug=Debug +esp32p4_4ds_mipi_round.menu.DebugLevel.debug.build.code_debug=4 +esp32p4_4ds_mipi_round.menu.DebugLevel.verbose=Verbose +esp32p4_4ds_mipi_round.menu.DebugLevel.verbose.build.code_debug=5 + +esp32p4_4ds_mipi_round.menu.EraseFlash.none=Disabled +esp32p4_4ds_mipi_round.menu.EraseFlash.none.upload.erase_cmd= +esp32p4_4ds_mipi_round.menu.EraseFlash.all=Enabled +esp32p4_4ds_mipi_round.menu.EraseFlash.all.upload.erase_cmd=-e + +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34r=ESP32-P4-34R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34r.build.DisplayModel=ESP32P4_34R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34r_clb=ESP32-P4-34R-CLB +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34r_clb.build.DisplayModel=ESP32P4_34R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34rct=ESP32-P4-34RCT +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34rct.build.DisplayModel=ESP32P4_34RCT +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34rct_clb=ESP32-P4-34RCT-CLB +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_34rct_clb.build.DisplayModel=ESP32P4_34RCT +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40r=ESP32-P4-40R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40r.build.DisplayModel=ESP32P4_40R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40r_clb=ESP32-P4-40R-CLB +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40r_clb.build.DisplayModel=ESP32P4_40R +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40rct=ESP32-P4-40RCT +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40rct.build.DisplayModel=ESP32P4_40RCT +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40rct_clb=ESP32-P4-40RCT-CLB +esp32p4_4ds_mipi_round.menu.DisplayModel.esp32p4_40rct_clb.build.DisplayModel=ESP32P4_40RCT + +esp32p4_4ds_mipi_round.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 @@ -52716,7 +53529,7 @@ 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/190KB SPIFFS) +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 @@ -52792,6 +53605,467 @@ axiometa_pixie_m1.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzbo ############################################################## +# Axiometa Genesis One - Based on ESP32-S3-WROOM-1-N8R2 +# 8MB Quad SPI Flash, 2MB Quad SPI PSRAM + +axiometa_genesis_one.name=Axiometa Genesis One + +axiometa_genesis_one.bootloader.tool=esptool_py +axiometa_genesis_one.bootloader.tool.default=esptool_py + +axiometa_genesis_one.upload.tool=esptool_py +axiometa_genesis_one.upload.tool.default=esptool_py +axiometa_genesis_one.upload.tool.network=esp_ota + +axiometa_genesis_one.upload.maximum_size=1310720 +axiometa_genesis_one.upload.maximum_data_size=327680 +axiometa_genesis_one.upload.flags= +axiometa_genesis_one.upload.extra_flags= +axiometa_genesis_one.upload.use_1200bps_touch=false +axiometa_genesis_one.upload.wait_for_upload_port=false + +axiometa_genesis_one.serial.disableDTR=false +axiometa_genesis_one.serial.disableRTS=false + +axiometa_genesis_one.build.tarch=xtensa +axiometa_genesis_one.build.bootloader_addr=0x0 +axiometa_genesis_one.build.target=esp32s3 +axiometa_genesis_one.build.mcu=esp32s3 +axiometa_genesis_one.build.core=esp32 +axiometa_genesis_one.build.variant=axiometa_genesis_one +axiometa_genesis_one.build.board=AXIOMETA_GENESIS_ONE + +# Hardware Configuration (ESP32-S3-WROOM-1-N8R2) +axiometa_genesis_one.build.usb_mode=1 +axiometa_genesis_one.build.cdc_on_boot=1 +axiometa_genesis_one.build.msc_on_boot=0 +axiometa_genesis_one.build.dfu_on_boot=0 +axiometa_genesis_one.build.f_cpu=240000000L +axiometa_genesis_one.build.flash_size=8MB +axiometa_genesis_one.build.flash_freq=80m +axiometa_genesis_one.build.flash_mode=dio +axiometa_genesis_one.build.boot=qio +axiometa_genesis_one.build.boot_freq=80m +axiometa_genesis_one.build.partitions=default_8MB +axiometa_genesis_one.build.defines=-DBOARD_HAS_PSRAM +axiometa_genesis_one.build.loop_core= +axiometa_genesis_one.build.event_core= +axiometa_genesis_one.build.psram_type=qspi +axiometa_genesis_one.build.memory_type={build.boot}_{build.psram_type} + +## JTAG Adapter - N8R2 Compatible +axiometa_genesis_one.menu.JTAGAdapter.default=Disabled +axiometa_genesis_one.menu.JTAGAdapter.default.build.copy_jtag_files=0 +axiometa_genesis_one.menu.JTAGAdapter.builtin=Integrated USB JTAG +axiometa_genesis_one.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +axiometa_genesis_one.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +axiometa_genesis_one.menu.JTAGAdapter.external=FTDI Adapter +axiometa_genesis_one.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +axiometa_genesis_one.menu.JTAGAdapter.external.build.copy_jtag_files=1 +axiometa_genesis_one.menu.JTAGAdapter.bridge=ESP USB Bridge +axiometa_genesis_one.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +axiometa_genesis_one.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +## PSRAM - N8R2 has 2MB QSPI PSRAM +axiometa_genesis_one.menu.PSRAM.enabled=QSPI PSRAM (Enabled) +axiometa_genesis_one.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +axiometa_genesis_one.menu.PSRAM.enabled.build.psram_type=qspi +axiometa_genesis_one.menu.PSRAM.disabled=Disabled +axiometa_genesis_one.menu.PSRAM.disabled.build.defines= +axiometa_genesis_one.menu.PSRAM.disabled.build.psram_type=qspi + +## Flash Mode - N8R2 Compatible (QSPI Flash) +axiometa_genesis_one.menu.FlashMode.qio=QIO 80MHz +axiometa_genesis_one.menu.FlashMode.qio.build.flash_mode=dio +axiometa_genesis_one.menu.FlashMode.qio.build.boot=qio +axiometa_genesis_one.menu.FlashMode.qio.build.boot_freq=80m +axiometa_genesis_one.menu.FlashMode.qio.build.flash_freq=80m +axiometa_genesis_one.menu.FlashMode.qio120=QIO 120MHz +axiometa_genesis_one.menu.FlashMode.qio120.build.flash_mode=dio +axiometa_genesis_one.menu.FlashMode.qio120.build.boot=qio +axiometa_genesis_one.menu.FlashMode.qio120.build.boot_freq=120m +axiometa_genesis_one.menu.FlashMode.qio120.build.flash_freq=80m +axiometa_genesis_one.menu.FlashMode.dio=DIO 80MHz +axiometa_genesis_one.menu.FlashMode.dio.build.flash_mode=dio +axiometa_genesis_one.menu.FlashMode.dio.build.boot=dio +axiometa_genesis_one.menu.FlashMode.dio.build.boot_freq=80m +axiometa_genesis_one.menu.FlashMode.dio.build.flash_freq=80m + +## CPU Core Assignment +axiometa_genesis_one.menu.LoopCore.1=Core 1 +axiometa_genesis_one.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +axiometa_genesis_one.menu.LoopCore.0=Core 0 +axiometa_genesis_one.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +axiometa_genesis_one.menu.EventsCore.1=Core 1 +axiometa_genesis_one.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +axiometa_genesis_one.menu.EventsCore.0=Core 0 +axiometa_genesis_one.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +## USB Mode - Both modes work on N8R2 +axiometa_genesis_one.menu.USBMode.hwcdc=Hardware CDC and JTAG +axiometa_genesis_one.menu.USBMode.hwcdc.build.usb_mode=1 +axiometa_genesis_one.menu.USBMode.default=USB-OTG (TinyUSB) +axiometa_genesis_one.menu.USBMode.default.build.usb_mode=0 + +## CDC On Boot +axiometa_genesis_one.menu.CDCOnBoot.cdc=Enabled +axiometa_genesis_one.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +axiometa_genesis_one.menu.CDCOnBoot.default=Disabled +axiometa_genesis_one.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +## MSC On Boot (Only works with USB-OTG mode) +axiometa_genesis_one.menu.MSCOnBoot.default=Disabled +axiometa_genesis_one.menu.MSCOnBoot.default.build.msc_on_boot=0 +axiometa_genesis_one.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +axiometa_genesis_one.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +## DFU On Boot (Only works with USB-OTG mode) +axiometa_genesis_one.menu.DFUOnBoot.default=Disabled +axiometa_genesis_one.menu.DFUOnBoot.default.build.dfu_on_boot=0 +axiometa_genesis_one.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +axiometa_genesis_one.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +## Upload Mode +axiometa_genesis_one.menu.UploadMode.default=UART0 / Hardware CDC +axiometa_genesis_one.menu.UploadMode.default.upload.use_1200bps_touch=false +axiometa_genesis_one.menu.UploadMode.default.upload.wait_for_upload_port=false +axiometa_genesis_one.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +axiometa_genesis_one.menu.UploadMode.cdc.upload.use_1200bps_touch=true +axiometa_genesis_one.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +## Partition Schemes - 8MB Flash Compatible +axiometa_genesis_one.menu.PartitionScheme.default_8MB=Default 8MB with spiffs (3MB APP/1.5MB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +axiometa_genesis_one.menu.PartitionScheme.default_8MB.upload.maximum_size=3145728 +axiometa_genesis_one.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.default.build.partitions=default +axiometa_genesis_one.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +axiometa_genesis_one.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +axiometa_genesis_one.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.minimal.build.partitions=minimal +axiometa_genesis_one.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +axiometa_genesis_one.menu.PartitionScheme.no_fs.build.partitions=no_fs +axiometa_genesis_one.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +axiometa_genesis_one.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.no_ota.build.partitions=no_ota +axiometa_genesis_one.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +axiometa_genesis_one.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +axiometa_genesis_one.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +axiometa_genesis_one.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +axiometa_genesis_one.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +axiometa_genesis_one.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +axiometa_genesis_one.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +axiometa_genesis_one.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +axiometa_genesis_one.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +axiometa_genesis_one.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.huge_app.build.partitions=huge_app +axiometa_genesis_one.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +axiometa_genesis_one.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +axiometa_genesis_one.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +axiometa_genesis_one.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +axiometa_genesis_one.menu.PartitionScheme.rainmaker=RainMaker 4MB +axiometa_genesis_one.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +axiometa_genesis_one.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +axiometa_genesis_one.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB +axiometa_genesis_one.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB +axiometa_genesis_one.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=3145728 +axiometa_genesis_one.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +axiometa_genesis_one.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +axiometa_genesis_one.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +axiometa_genesis_one.menu.PartitionScheme.custom=Custom +axiometa_genesis_one.menu.PartitionScheme.custom.build.partitions= +axiometa_genesis_one.menu.PartitionScheme.custom.upload.maximum_size=8388608 + +## CPU Frequency +axiometa_genesis_one.menu.CPUFreq.240=240MHz (WiFi) +axiometa_genesis_one.menu.CPUFreq.240.build.f_cpu=240000000L +axiometa_genesis_one.menu.CPUFreq.160=160MHz (WiFi) +axiometa_genesis_one.menu.CPUFreq.160.build.f_cpu=160000000L +axiometa_genesis_one.menu.CPUFreq.80=80MHz (WiFi) +axiometa_genesis_one.menu.CPUFreq.80.build.f_cpu=80000000L +axiometa_genesis_one.menu.CPUFreq.40=40MHz +axiometa_genesis_one.menu.CPUFreq.40.build.f_cpu=40000000L +axiometa_genesis_one.menu.CPUFreq.20=20MHz +axiometa_genesis_one.menu.CPUFreq.20.build.f_cpu=20000000L +axiometa_genesis_one.menu.CPUFreq.10=10MHz +axiometa_genesis_one.menu.CPUFreq.10.build.f_cpu=10000000L + +## Upload Speed +axiometa_genesis_one.menu.UploadSpeed.921600=921600 +axiometa_genesis_one.menu.UploadSpeed.921600.upload.speed=921600 +axiometa_genesis_one.menu.UploadSpeed.115200=115200 +axiometa_genesis_one.menu.UploadSpeed.115200.upload.speed=115200 +axiometa_genesis_one.menu.UploadSpeed.256000.windows=256000 +axiometa_genesis_one.menu.UploadSpeed.256000.upload.speed=256000 +axiometa_genesis_one.menu.UploadSpeed.230400.windows.upload.speed=256000 +axiometa_genesis_one.menu.UploadSpeed.230400=230400 +axiometa_genesis_one.menu.UploadSpeed.230400.upload.speed=230400 +axiometa_genesis_one.menu.UploadSpeed.460800.linux=460800 +axiometa_genesis_one.menu.UploadSpeed.460800.macosx=460800 +axiometa_genesis_one.menu.UploadSpeed.460800.upload.speed=460800 +axiometa_genesis_one.menu.UploadSpeed.512000.windows=512000 +axiometa_genesis_one.menu.UploadSpeed.512000.upload.speed=512000 + +## Debug Level +axiometa_genesis_one.menu.DebugLevel.none=None +axiometa_genesis_one.menu.DebugLevel.none.build.code_debug=0 +axiometa_genesis_one.menu.DebugLevel.error=Error +axiometa_genesis_one.menu.DebugLevel.error.build.code_debug=1 +axiometa_genesis_one.menu.DebugLevel.warn=Warn +axiometa_genesis_one.menu.DebugLevel.warn.build.code_debug=2 +axiometa_genesis_one.menu.DebugLevel.info=Info +axiometa_genesis_one.menu.DebugLevel.info.build.code_debug=3 +axiometa_genesis_one.menu.DebugLevel.debug=Debug +axiometa_genesis_one.menu.DebugLevel.debug.build.code_debug=4 +axiometa_genesis_one.menu.DebugLevel.verbose=Verbose +axiometa_genesis_one.menu.DebugLevel.verbose.build.code_debug=5 + +## Erase Flash +axiometa_genesis_one.menu.EraseFlash.none=Disabled +axiometa_genesis_one.menu.EraseFlash.none.upload.erase_cmd= +axiometa_genesis_one.menu.EraseFlash.all=Enabled +axiometa_genesis_one.menu.EraseFlash.all.upload.erase_cmd=-e + +## Zigbee Mode +axiometa_genesis_one.menu.ZigbeeMode.default=Disabled +axiometa_genesis_one.menu.ZigbeeMode.default.build.zigbee_mode= +axiometa_genesis_one.menu.ZigbeeMode.default.build.zigbee_libs= +axiometa_genesis_one.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +axiometa_genesis_one.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +axiometa_genesis_one.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 @@ -52866,7 +54140,7 @@ soldered_nula_mini_esp32c6.menu.PartitionScheme.noota_3gffat.upload.maximum_size 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/190KB SPIFFS) +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 @@ -52955,3 +54229,149 @@ soldered_nula_mini_esp32c6.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ 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/Esp.cpp b/cores/esp32/Esp.cpp index 5f5a7a353e8..023afd80d8b 100644 --- a/cores/esp32/Esp.cpp +++ b/cores/esp32/Esp.cpp @@ -83,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 @@ -365,7 +368,7 @@ uint32_t EspClass::getFlashChipSpeed(void) { } FlashMode_t EspClass::getFlashChipMode(void) { -#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32P4 || CONFIG_IDF_TARGET_ESP32C5 +#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); #elif CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32C6 uint32_t spi_ctrl = REG_READ(DR_REG_SPI0_BASE + 0x8); @@ -449,6 +452,22 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t flashByte) { 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 /* diff --git a/cores/esp32/HardwareSerial.cpp b/cores/esp32/HardwareSerial.cpp index 3651667fe07..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)); @@ -483,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 @@ -572,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/MacAddress.cpp b/cores/esp32/MacAddress.cpp index 8b4fab1781a..279b2512633 100644 --- a/cores/esp32/MacAddress.cpp +++ b/cores/esp32/MacAddress.cpp @@ -32,6 +32,24 @@ MacAddress::MacAddress(const String &macstr) { fromString(macstr.c_str()); } +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +MacAddress::MacAddress(std::initializer_list list) { + size_t size = list.size(); + memset(_mac.bytes, 0, sizeof(_mac.bytes)); + if (size == 6) { + _type = MAC6; + } else if (size == 8) { + _type = MAC8; + } else { + // Default to MAC6 and keep the rest of the bytes as 0 + _type = MAC6; + return; + } + + memcpy(_mac.bytes, list.begin(), size); +} +#endif + MacAddress::MacAddress(uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5, uint8_t b6) { _type = MAC6; memset(_mac.bytes, 0, sizeof(_mac.bytes)); diff --git a/cores/esp32/MacAddress.h b/cores/esp32/MacAddress.h index 5b42649c774..1fb7029dbfd 100644 --- a/cores/esp32/MacAddress.h +++ b/cores/esp32/MacAddress.h @@ -23,6 +23,9 @@ #include #include #include +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#include +#endif enum MACType { MAC6, @@ -56,6 +59,12 @@ class MacAddress : public Printable { MacAddress(const char *macstr); MacAddress(const String &macstr); +#ifdef __GXX_EXPERIMENTAL_CXX0X__ + // Initializer list constructor for {0xAA, 0xBB, ...} syntax + // This has higher priority than String conversion, preventing ambiguity + MacAddress(std::initializer_list list); +#endif + virtual ~MacAddress() {} bool fromString(const char *buf); diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 7210dfbfbc4..ead5721b9e3 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -59,6 +59,13 @@ String::String(StringSumHelper &&rval) { init(); move(rval); } + +String::String(std::initializer_list list) { + init(); + if (list.size() > 0) { + copy(list.begin(), list.size()); + } +} #endif String::String(char c) { diff --git a/cores/esp32/WString.h b/cores/esp32/WString.h index a8327f40de0..d9cc7859230 100644 --- a/cores/esp32/WString.h +++ b/cores/esp32/WString.h @@ -29,6 +29,9 @@ #include #include #include +#ifdef __GXX_EXPERIMENTAL_CXX0X__ +#include +#endif // A pure abstract class forward used as a means to proide a unique pointer type // but really is never defined. @@ -58,6 +61,7 @@ class String { String(const char *cstr, unsigned int length); #ifdef __GXX_EXPERIMENTAL_CXX0X__ String(const uint8_t *cstr, unsigned int length) : String(reinterpret_cast(cstr), length) {} + String(std::initializer_list list); #endif String(const String &str); String(const __FlashStringHelper *str) : String(reinterpret_cast(str)) {} diff --git a/cores/esp32/chip-debug-report.cpp b/cores/esp32/chip-debug-report.cpp index 753e7346775..414dd108a63 100644 --- a/cores/esp32/chip-debug-report.cpp +++ b/cores/esp32/chip-debug-report.cpp @@ -69,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"); @@ -292,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..4fb62ad7518 100644 --- a/cores/esp32/esp32-hal-adc.c +++ b/cores/esp32/esp32-hal-adc.c @@ -20,6 +20,36 @@ #include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_continuous.h" #include "esp_adc/adc_cali_scheme.h" +#include "esp_heap_caps.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 @@ -75,11 +105,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 +160,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 +178,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 +346,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,45 +399,45 @@ 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; + // Guard against double-cleanup: check if already cleaned up if (adc_handle[adc_unit].adc_continuous_handle == NULL) { return true; - } else { - esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle); + } + + // Clean up ADC driver + esp_err_t err = adc_continuous_deinit(adc_handle[adc_unit].adc_continuous_handle); + if (err != ESP_OK) { + return false; + } + adc_handle[adc_unit].adc_continuous_handle = NULL; + + // Clean up calibration handle if exists + if (adc_handle[adc_unit].adc_cali_handle != NULL) { +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle); if (err != ESP_OK) { return false; } - adc_handle[adc_unit].adc_continuous_handle = NULL; - if (adc_handle[adc_unit].adc_cali_handle != NULL) { -#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - err = adc_cali_delete_scheme_curve_fitting(adc_handle[adc_unit].adc_cali_handle); - if (err != ESP_OK) { - return false; - } -#elif (!defined(CONFIG_IDF_TARGET_ESP32H2) && !defined(CONFIG_IDF_TARGET_ESP32P4)) - err = adc_cali_delete_scheme_line_fitting(adc_handle[adc_unit].adc_cali_handle); - if (err != ESP_OK) { - return false; - } -#endif +#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; - - //set all used pins to INIT state - for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(adc_unit); channel++) { - int io_pin; - adc_oneshot_channel_to_io(adc_unit, channel, &io_pin); - if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) { - if (!perimanClearPinBus(io_pin)) { - return false; - } - } - } } + + // Don't call perimanClearPinBus() here - the peripheral manager already handles it. + // This callback is only responsible for cleaning up the IDF's ADC driver and calibration handles. + // It does NOT free the adc_result buffer. The caller is responsible for freeing adc_result. return true; } @@ -508,6 +547,14 @@ bool analogContinuous(const uint8_t pins[], size_t pins_count, uint32_t conversi } #endif +#if CONFIG_IDF_TARGET_ESP32P4 + // Align conversion frame size to cache line size (required for DMA on targets with cache) + uint32_t alignment_remainder = adc_handle[adc_unit].conversion_frame_size % CONFIG_CACHE_L1_CACHE_LINE_SIZE; + if (alignment_remainder != 0) { + adc_handle[adc_unit].conversion_frame_size += (CONFIG_CACHE_L1_CACHE_LINE_SIZE - alignment_remainder); + } +#endif + adc_handle[adc_unit].buffer_size = adc_handle[adc_unit].conversion_frame_size * 2; //Conversion frame size buffer cant be bigger than 4092 bytes @@ -536,7 +583,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 +599,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,13 +627,26 @@ 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]; uint32_t read_count[used_adc_channels]; - uint8_t adc_read[adc_handle[ADC_UNIT_1].conversion_frame_size]; - memset(adc_read, 0xcc, sizeof(adc_read)); + + // Allocate DMA buffer with cache line alignment (required for ESP32-P4 and other targets with cache) + size_t buffer_size = adc_handle[ADC_UNIT_1].conversion_frame_size; +#if CONFIG_IDF_TARGET_ESP32P4 + uint8_t *adc_read = (uint8_t *)heap_caps_aligned_alloc(CONFIG_CACHE_L1_CACHE_LINE_SIZE, buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); +#else + uint8_t *adc_read = (uint8_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL); +#endif + if (adc_read == NULL) { + log_e("Failed to allocate DMA buffer"); + *buffer = NULL; + return false; + } + + memset(adc_read, 0xcc, buffer_size); memset(read_raw, 0, sizeof(read_raw)); memset(read_count, 0, sizeof(read_count)); @@ -594,6 +657,8 @@ bool analogContinuousRead(adc_continuous_data_t **buffer, uint32_t timeout_ms) { } else { log_e("Reading data failed with error: %X", err); } + free(adc_read); + adc_read = NULL; *buffer = NULL; return false; } @@ -632,6 +697,8 @@ bool analogContinuousRead(adc_continuous_data_t **buffer, uint32_t timeout_ms) { } } + free(adc_read); + adc_read = NULL; *buffer = adc_result; return true; @@ -664,16 +731,29 @@ bool analogContinuousStop() { } bool analogContinuousDeinit() { - if (adc_handle[ADC_UNIT_1].adc_continuous_handle != NULL) { - esp_err_t err = adc_continuous_deinit(adc_handle[ADC_UNIT_1].adc_continuous_handle); - if (err != ESP_OK) { - return false; + if (adc_handle[ADC_UNIT_1].adc_continuous_handle == NULL) { + log_i("ADC Continuous was not initialized"); + return true; + } + + // Clear all used pins from peripheral manager + // This will trigger adcContinuousDetachBus() callback which cleans up the ADC driver + for (uint8_t channel = 0; channel < SOC_ADC_CHANNEL_NUM(ADC_UNIT_1); channel++) { + int io_pin; + adc_oneshot_channel_to_io(ADC_UNIT_1, channel, &io_pin); + if (perimanGetPinBusType(io_pin) == ESP32_BUS_TYPE_ADC_CONT) { + if (!perimanClearPinBus(io_pin)) { + return false; + } } + } + + // Free the result buffer (callback doesn't do this) + if (adc_result != NULL) { free(adc_result); - adc_handle[ADC_UNIT_1].adc_continuous_handle = NULL; - } else { - log_i("ADC Continuous was not initialized"); + adc_result = NULL; } + return true; } 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 197f789d94d..4a7fe6d1e75 100644 --- a/cores/esp32/esp32-hal-gpio.c +++ b/cores/esp32/esp32-hal-gpio.c @@ -127,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 index 0bb8023432a..b5ab854dbc8 100644 --- a/cores/esp32/esp32-hal-hosted.c +++ b/cores/esp32/esp32-hal-hosted.c @@ -17,10 +17,13 @@ #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(); +// 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; @@ -46,6 +49,138 @@ static sdio_pin_config_t sdio_pin_config = { #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"); @@ -62,13 +197,26 @@ static bool hostedInit() { 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!"); + 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 @@ -101,8 +249,21 @@ static bool hostedDeinit() { 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 hostedInit(); + return true; } bool hostedInitWiFi() { @@ -113,6 +274,16 @@ bool hostedInitWiFi() { 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(); diff --git a/cores/esp32/esp32-hal-hosted.h b/cores/esp32/esp32-hal-hosted.h index 0a916bfac85..1b329fcb469 100644 --- a/cores/esp32/esp32-hal-hosted.h +++ b/cores/esp32/esp32-hal-hosted.h @@ -44,6 +44,14 @@ 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 } 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 1f23b7832f7..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 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 41e2cf71543..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 diff --git a/cores/esp32/esp32-hal-periman.c b/cores/esp32/esp32-hal-periman.c index 2ba8ffde5f7..bec88b0fdeb 100644 --- a/cores/esp32/esp32-hal-periman.c +++ b/cores/esp32/esp32-hal-periman.c @@ -236,11 +236,30 @@ 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]; } diff --git a/cores/esp32/esp32-hal-periman.h b/cores/esp32/esp32-hal-periman.h index 08b8c791ea7..12563718e19 100644 --- a/cores/esp32/esp32-hal-periman.h +++ b/cores/esp32/esp32-hal-periman.h @@ -134,6 +134,9 @@ 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); 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-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-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 44bde091be8..a28ee4994c4 100644 --- a/cores/esp32/esp32-hal-touch-ng.c +++ b/cores/esp32/esp32-hal-touch-ng.c @@ -117,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; @@ -129,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; @@ -395,7 +403,7 @@ 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; @@ -416,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 } // /* diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 3b88dae5901..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 @@ -60,6 +63,7 @@ struct uart_struct_t { bool _inverted; // UART inverted signal uint8_t _rxfifo_full_thrhd; // UART RX FIFO full threshold 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); } @@ -628,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) { @@ -659,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) { @@ -873,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 @@ -975,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) { diff --git a/cores/esp32/esp32-hal-uart.h b/cores/esp32/esp32-hal-uart.h index 3af0d7ab96c..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); diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 84c73577d3e..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; diff --git a/cores/esp32/esp_arduino_version.h b/cores/esp32/esp_arduino_version.h index 20cb911956d..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 2 +#define ESP_ARDUINO_VERSION_PATCH 4 /** * Macro to convert ARDUINO version number into an integer diff --git a/docs/_static/chatbot_widget.css b/docs/_static/chatbot_widget.css new file mode 100644 index 00000000000..2a9c1d8daf3 --- /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/_static/custom.css b/docs/_static/custom.css new file mode 100644 index 00000000000..e75ee37555a --- /dev/null +++ b/docs/_static/custom.css @@ -0,0 +1,24 @@ +/* Custom styles for Arduino ESP32 documentation */ + +/* Wide table support - make content container wider */ +/* +.document { + max-width: 1100px !important; +} + +.wy-nav-content { + max-width: 1100px !important; +} +*/ + +/* Make tables scrollable when they exceed page width */ +.table-wrap { + overflow-x: auto; + max-width: 100%; +} + +.table-wrap table { + font-size: 0.9em; + width: auto !important; + display: table; +} diff --git a/docs/_static/matter_erase_flash.png b/docs/_static/matter_erase_flash.png new file mode 100644 index 00000000000..621fbe9a5b2 Binary files /dev/null and b/docs/_static/matter_erase_flash.png differ diff --git a/docs/_static/matter_partition_scheme.png b/docs/_static/matter_partition_scheme.png new file mode 100644 index 00000000000..f835e8b7268 Binary files /dev/null and b/docs/_static/matter_partition_scheme.png differ diff --git a/docs/conf_common.py b/docs/conf_common.py index 1ef2e5ee2e6..7f6e9324d29 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -4,8 +4,11 @@ # Used for substituting variables in the documentation rst_prolog = """ -.. |version| replace:: 3.3.2 +.. |version| replace:: 3.3.4 .. |idf_version| replace:: 5.5 +.. |no| replace:: ❌ +.. |yes| replace:: ✅ +.. |n/a| replace:: ➖ """ languages = ["en"] @@ -28,6 +31,12 @@ html_static_path = ["../_static"] +html_js_files = ["../_static/chatbot_widget_en.js"] +html_css_files = [ + "../_static/chatbot_widget.css", + "../_static/custom.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/boards/boards.rst b/docs/en/boards/boards.rst index 407b019a78b..5de14119d6d 100644 --- a/docs/en/boards/boards.rst +++ b/docs/en/boards/boards.rst @@ -18,10 +18,16 @@ The ESP32 is divided by family: * ESP32 * Wi-Fi, BT and BLE 4 +* ESP32-C2 [#rebuild-required]_ + * Wi-Fi and BLE 5 * ESP32-C3 * Wi-Fi and BLE 5 +* ESP32-C5 + * Dual-band Wi-Fi 6, BLE 5 and IEEE 802.15.4 * ESP32-C6 - * Wi-Fi, BLE 5 and IEEE 802.15.4 + * Wi-Fi 6, BLE 5 and IEEE 802.15.4 +* ESP32-C61 [#rebuild-required]_ + * Wi-Fi 6, BLE 5 * ESP32-H2 * BLE 5 and IEEE 802.15.4 * ESP32-P4 @@ -31,6 +37,8 @@ The ESP32 is divided by family: * ESP32-S3 * Wi-Fi and BLE 5 +.. [#rebuild-required] This SoC is only supported using Arduino as an ESP-IDF component or by rebuilding the static libraries. + For each family, we have SoC variants with some differentiation. The differences are more about the embedded flash and its size and the number of the cores (dual or single). The modules use the SoC internally, including the external flash, PSRAM (in some models) and other essential electronic components. Essentially, all diff --git a/docs/en/common/datasheet.inc b/docs/en/common/datasheet.inc index 7086a12d1a8..a4f580cd826 100644 --- a/docs/en/common/datasheet.inc +++ b/docs/en/common/datasheet.inc @@ -4,7 +4,9 @@ Datasheet * `ESP32`_ (Datasheet) * `ESP32-C2`_ (Datasheet) * `ESP32-C3`_ (Datasheet) +* `ESP32-C5`_ (Datasheet) * `ESP32-C6`_ (Datasheet) +* `ESP32-C61`_ (Datasheet) * `ESP32-H2`_ (Datasheet) * `ESP32-P4`_ (Datasheet) * `ESP32-S2`_ (Datasheet) @@ -14,7 +16,9 @@ Datasheet .. _ESP32: https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf .. _ESP32-C2: https://www.espressif.com/sites/default/files/documentation/esp8684_datasheet_en.pdf .. _ESP32-C3: https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf +.. _ESP32-C5: https://www.espressif.com/sites/default/files/documentation/esp32-c5_datasheet_en.pdf .. _ESP32-C6: https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf +.. _ESP32-C61: https://www.espressif.com/sites/default/files/documentation/esp32-c61_datasheet_en.pdf .. _ESP32-H2: https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf .. _ESP32-P4: https://www.espressif.com/sites/default/files/documentation/esp32-p4_datasheet_en.pdf .. _ESP32-S2: https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf diff --git a/docs/en/contributing.rst b/docs/en/contributing.rst index 0a2ff38b95f..08b768310d0 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: @@ -181,31 +174,25 @@ Currently, the default FQBNs are: * ``espressif:esp32:esp32c3`` * ``espressif:esp32:esp32c6`` * ``espressif:esp32:esp32h2`` -* ``espressif:esp32:esp32p4:USBMode=default`` +* ``espressif:esp32:esp32p4:USBMode=default,ChipVariant=postv3`` -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 @@ -394,9 +388,9 @@ 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. * Any other files that are needed for the test suite. @@ -405,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. @@ -419,7 +413,9 @@ 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 `_ @@ -429,10 +425,10 @@ The ``ci.json`` file is used to specify how the test suite and sketches will han 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/getting_started.rst b/docs/en/getting_started.rst index 1b0f1bba87a..86b2fd692a1 100644 --- a/docs/en/getting_started.rst +++ b/docs/en/getting_started.rst @@ -39,6 +39,7 @@ SoC Stable Development Datasheet ========== ====== =========== ================================= ESP32 Yes Yes `ESP32`_ ESP32-C3 Yes Yes `ESP32-C3`_ +ESP32-C5 Yes Yes `ESP32-C5`_ ESP32-C6 Yes Yes `ESP32-C6`_ ESP32-H2 Yes Yes `ESP32-H2`_ ESP32-P4 Yes Yes `ESP32-P4`_ @@ -47,7 +48,7 @@ ESP32-S3 Yes Yes `ESP32-S3`_ ========== ====== =========== ================================= .. 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 `_ or the `Lib Builder documentation `_, respectively. 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..c4032c786c7 100644 --- a/docs/en/libraries.rst +++ b/docs/en/libraries.rst @@ -2,77 +2,114 @@ Libraries ######### -Here is where the Libraries API's descriptions are located: - -Supported Peripherals ---------------------- - -Currently, the Arduino ESP32 supports the following peripherals with Arduino APIs. - -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Peripheral | ESP32 | C3 | C6 | H2 | P4 | S2 | S3 | Notes | -+===============+=======+=======+=======+=======+=======+=======+=======+=======+ -| ADC | Yes | Yes | Yes | Yes | Yes | Yes | Yes | (1) | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| BT Classic | Yes | N/A | N/A | N/A | N/A | N/A | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| BLE | Yes | Yes | Yes | Yes | No | N/A | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| DAC | Yes | N/A | N/A | N/A | Yes | Yes | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Ethernet | Yes | N/A | N/A | N/A | Yes | N/A | N/A | (2) | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| GPIO | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Hall Sensor | N/A | N/A | N/A | N/A | N/A | N/A | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| I2C | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| I2S | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| LEDC | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| MIPI | N/A | N/A | N/A | N/A | No | N/A | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Motor PWM | No | N/A | N/A | N/A | N/A | N/A | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| MSPI | N/A | N/A | N/A | N/A | No | N/A | N/A | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Pulse Counter | No | No | No | No | No | No | No | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| RMT | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| SDIO | No | No | No | No | No | No | No | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| SDMMC | Yes | N/A | N/A | N/A | N/A | N/A | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Timer | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Temp. Sensor | N/A | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Touch | Yes | N/A | N/A | N/A | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| TWAI | No | No | No | No | No | No | No | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| UART | Yes | Yes | Yes | Yes | Yes | Yes | Yes | | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| USB | N/A | Yes | Yes | Yes | Yes | Yes | Yes | (3) | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ -| Wi-Fi | Yes | Yes | Yes | N/A | Yes | Yes | Yes | (4) | -+---------------+-------+-------+-------+-------+-------+-------+-------+-------+ - -Notes -^^^^^ - -(1) ESP32-P4 calibration schemes not supported yet in IDF and ADC Continuous also lacks IDF support. - -(2) SPI Ethernet is supported by all ESP32 families and RMII only for ESP32 and ESP32-P4. - -(3) ESP32-C3, C6, H2 only support USB CDC/JTAG - -(4) ESP32-P4 only supports Wi-Fi through another SoC by using ``esp_hosted``. - -.. note:: Some peripherals are not available for all ESP32 families. To see more details about it, see the corresponding SoC at `Product Selector `_ page. +Arduino libraries help you use the features of the ESP32 family of chips with the familiar Arduino API. + +Supported Features and Peripherals +---------------------------------- + +Currently, the Arduino ESP32 supports almost everything available on the ESP32 family with an Arduino-like API. + +Not all features are available on all SoCs. Please check the `Product Selector `_ page +for more details. + +Here is a matrix of the library support status for the main features and peripherals per SoC: + +- |yes| Supported through the Arduino Core +- |no| Not supported through the Arduino Core. It can still be used through the ESP-IDF API, but might require rebuilding the static libraries. +- |n/a| Not available on the SoC + +.. rst-class:: table-wrap + +.. Using substitutions rather than emojis directly because in macOS vscode the emojis don't take a fixed space in the text + and the table looks weird and hard to edit. This is a workaround to make the table easier to edit. Just write + |yes|, |no|, |n/a| instead of emojis. + ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Feature | ESP32 | C2 | C3 | C5 | C6 | C61 | H2 | P4 | S2 | S3 | ++======================+=======+=======+=======+=======+=======+=======+=======+=======+=======+=======+ +| ADC [1]_ | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| BT Classic | |yes| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| BLE [2]_ | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |n/a| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| DAC | |yes| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |yes| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| ESP-NOW [3]_ | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |n/a| | |n/a| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Ethernet [4]_ | |yes| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |yes| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| GPIO | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Hall Sensor | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| I2C | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| I2S | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| I3C | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |no| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| LEDC | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Matter (Thread) [6]_ | |n/a| | |n/a| | |n/a| | |yes| | |yes| | |n/a| | |yes| | |n/a| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Matter (Wi-Fi) | |yes| | |no| | |yes| | |yes| | |yes| | |no| | |n/a| | |n/a| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| MIPI CSI | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |no| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| MIPI DSI | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |no| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Motor PWM | |no| | |n/a| | |n/a| | |no| | |no| | |n/a| | |no| | |no| | |n/a| | |no| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| MSPI | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |no| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Pulse Counter | |no| | |no| | |no| | |no| | |no| | |no| | |no| | |no| | |no| | |no| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| RMT | |yes| | |yes| | |yes| | |yes| | |yes| | |n/a| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| SDIO | |no| | |n/a| | |n/a| | |no| | |no| | |no| | |n/a| | |no| | |n/a| | |no| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| SDMMC | |yes| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| PSRAM | |yes| | |n/a| | |n/a| | |yes| | |n/a| | |yes| | |n/a| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Temp. Sensor | |n/a| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Thread | |n/a| | |n/a| | |n/a| | |yes| | |yes| | |n/a| | |yes| | |n/a| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Timer | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Touch | |yes| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| TWAI/CAN-FD | |no| | |n/a| | |no| | |no| | |no| | |n/a| | |no| | |no| | |no| | |no| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| UART | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| USB OTG | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |n/a| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| USB Serial | |n/a| | |n/a| | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |n/a| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Wi-Fi [2]_ | |yes| | |yes| | |yes| | |yes| | |yes| | |yes| | |n/a| | |yes| | |yes| | |yes| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ +| Zigbee [5]_ | |n/a| | |n/a| | |n/a| | |yes| | |yes| | |n/a| | |yes| | |n/a| | |n/a| | |n/a| | ++----------------------+-------+-------+-------+-------+-------+-------+-------+-------+-------+-------+ + +.. [1] ESP32-P4 calibration schemes not supported yet in IDF and ADC Continuous also lacks IDF support. + +.. [2] ESP32-P4 only supports Wi-Fi and BLE through another SoC by using ``ESP-Hosted``. + +.. [3] ESP-NOW is not supported through ``ESP-Hosted``. + +.. [4] SPI Ethernet is supported by all ESP32 families and RMII only for ESP32 and ESP32-P4. + +.. [5] Non-native Zigbee SoCs can also run Zigbee, but must use another SoC (with Zigbee radio) as a RCP connected by UART/SPI. + Check the `Gateway example `_ for more details. + +.. [6] Matter over Thread is supported by our library but is not included in the pre-compiled libraries for ESP32-C6 and ESP32-C5. + In order to use Matter over Thread, you need to use Arduino as an ESP-IDF component or rebuild the static libraries. + Check the `Arduino_ESP_Matter_over_OpenThread example `_ for more details. + +.. note:: The ESP32-C2 and ESP32-C61 are only supported using Arduino as an ESP-IDF component or by rebuilding the static libraries. .. include:: common/datasheet.inc @@ -92,6 +129,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..a11afb1c28f --- /dev/null +++ b/docs/en/matter/ep_occupancy_sensor.rst @@ -0,0 +1,246 @@ +##################### +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) +* HoldTime attribute for configuring how long the sensor holds the "occupied" state +* HoldTimeLimits (min, max, default) for validation and controller guidance +* HoldTime change callback for real-time updates from Matter controllers +* 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. + +HoldTime Control +**************** + +setHoldTime +^^^^^^^^^^^ + +Sets the HoldTime value (in seconds). The HoldTime determines how long the sensor maintains the "occupied" state after the last detection. + +.. code-block:: arduino + + bool setHoldTime(uint16_t _holdTime_seconds); + +* ``_holdTime_seconds`` - HoldTime value in seconds + +**Important:** This function must be called after ``Matter.begin()`` has been called, as it requires the Matter event loop to be running. + +This function will return ``true`` if successful, ``false`` otherwise. + +getHoldTime +^^^^^^^^^^^ + +Gets the current HoldTime value (in seconds). + +.. code-block:: arduino + + uint16_t getHoldTime(); + +This function will return the current HoldTime value in seconds. + +setHoldTimeLimits +^^^^^^^^^^^^^^^^^ + +Sets the HoldTime limits (minimum, maximum, and default values). These limits define the valid range for HoldTime values and provide metadata for Matter controllers. + +.. code-block:: arduino + + bool setHoldTimeLimits(uint16_t _holdTimeMin_seconds, uint16_t _holdTimeMax_seconds, uint16_t _holdTimeDefault_seconds); + +* ``_holdTimeMin_seconds`` - Minimum HoldTime value in seconds +* ``_holdTimeMax_seconds`` - Maximum HoldTime value in seconds +* ``_holdTimeDefault_seconds`` - Default/recommended HoldTime value in seconds (informational metadata for controllers) + +**Important:** +* This function must be called after ``Matter.begin()`` has been called, as it requires the Matter event loop to be running. +* The ``holdTimeDefault_seconds`` parameter is informational metadata for Matter controllers (recommended default value). It does NOT automatically set the HoldTime attribute - use ``setHoldTime()`` to set the actual value. +* If the current HoldTime value is outside the new limits, it will be automatically adjusted to the nearest limit (minimum or maximum). + +This function will return ``true`` if successful, ``false`` otherwise. + +onHoldTimeChange +^^^^^^^^^^^^^^^^ + +Sets a callback function that will be called when the HoldTime value is changed by a Matter Controller. + +.. code-block:: arduino + + void onHoldTimeChange(HoldTimeChangeCB onHoldTimeChangeCB); + +* ``onHoldTimeChangeCB`` - Callback function of type ``HoldTimeChangeCB`` + +The callback function signature is: + +.. code-block:: arduino + + using HoldTimeChangeCB = std::function; + +The callback receives the new HoldTime value and can return ``true`` to accept the change or ``false`` to reject it. + +Example: + +.. code-block:: arduino + + OccupancySensor.onHoldTimeChange([](uint16_t holdTime_seconds) -> bool { + Serial.printf("HoldTime changed to %u seconds\n", holdTime_seconds); + return true; // Accept the change + }); + +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 +------- + +Basic Occupancy Sensor +********************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterOccupancySensor/MatterOccupancySensor.ino + :language: arduino + +Occupancy Sensor with HoldTime +******************************* + +For an example that demonstrates HoldTime functionality, see: + +.. literalinclude:: ../../../libraries/Matter/examples/MatterOccupancyWithHoldTime/MatterOccupancyWithHoldTime.ino + :language: arduino + +This example shows: +* How to configure HoldTimeLimits after ``Matter.begin()`` +* How to set and persist HoldTime values +* How to use the ``onHoldTimeChange()`` callback +* How to implement HoldTime expiration logic in sensor simulation 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/ep_window_covering.rst b/docs/en/matter/ep_window_covering.rst new file mode 100644 index 00000000000..3d976951691 --- /dev/null +++ b/docs/en/matter/ep_window_covering.rst @@ -0,0 +1,596 @@ +#################### +MatterWindowCovering +#################### + +About +----- + +The ``MatterWindowCovering`` class provides a window covering endpoint for Matter networks. This endpoint implements the Matter window covering standard for motorized blinds, shades, and other window coverings with lift and tilt control. + +**Features:** +* Lift position and percentage control (0-100%) +* Tilt position and percentage control (0-100%) +* Multiple window covering types support +* Callback support for open, close, lift, tilt, and stop commands +* Integration with Apple HomeKit, Amazon Alexa, and Google Home +* Matter standard compliance + +**Supported Window Covering Types:** +* ``ROLLERSHADE`` - Lift support +* ``ROLLERSHADE_2_MOTOR`` - Lift support +* ``ROLLERSHADE_EXTERIOR`` - Lift support +* ``ROLLERSHADE_EXTERIOR_2_MOTOR`` - Lift support +* ``DRAPERY`` - Lift support +* ``AWNING`` - Lift support +* ``SHUTTER`` - Tilt support +* ``BLIND_TILT_ONLY`` - Tilt support +* ``BLIND_LIFT_AND_TILT`` - Lift and Tilt support +* ``PROJECTOR_SCREEN`` - Lift support + +**Use Cases:** +* Motorized blinds +* Automated shades +* Smart window coverings +* Projector screens +* Awnings and drapes + +API Reference +------------- + +Constructor +*********** + +MatterWindowCovering +^^^^^^^^^^^^^^^^^^^^ + +Creates a new Matter window covering endpoint. + +.. code-block:: arduino + + MatterWindowCovering(); + +Initialization +************** + +begin +^^^^^ + +Initializes the Matter window covering endpoint with optional initial positions and covering type. + +.. code-block:: arduino + + bool begin(uint8_t liftPercent = 100, uint8_t tiltPercent = 0, WindowCoveringType_t coveringType = ROLLERSHADE); + +* ``liftPercent`` - Initial lift percentage (0-100, default: 100 = fully open) +* ``tiltPercent`` - Initial tilt percentage (0-100, default: 0) +* ``coveringType`` - Window covering type (default: ROLLERSHADE). This determines which features (lift, tilt, or both) are enabled. + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Lift percentage 0 means fully closed, 100 means fully open. Tilt percentage 0 means fully closed, 100 means fully open. The covering type must be specified during initialization to ensure the correct features (lift and/or tilt) are enabled. + +end +^^^ + +Stops processing Matter window covering events. + +.. code-block:: arduino + + void end(); + +Lift Position Control +********************* + +setLiftPosition +^^^^^^^^^^^^^^^ + +Sets the window covering lift position. + +.. code-block:: arduino + + bool setLiftPosition(uint16_t liftPosition); + +* ``liftPosition`` - Lift position value + +This function will return ``true`` if successful, ``false`` otherwise. + +getLiftPosition +^^^^^^^^^^^^^^^ + +Gets the current lift position. + +.. code-block:: arduino + + uint16_t getLiftPosition(); + +This function will return the current lift position. + +setLiftPercentage +^^^^^^^^^^^^^^^^^ + +Sets the window covering lift position as a percentage. This method updates the ``CurrentPositionLiftPercent100ths`` attribute, which reflects the device's actual position. The ``TargetPositionLiftPercent100ths`` attribute is set by Matter commands/apps when a new target is requested. + +.. code-block:: arduino + + bool setLiftPercentage(uint8_t liftPercent); + +* ``liftPercent`` - Lift percentage (0-100, where 0 is fully closed, 100 is fully open) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** When the device reaches the target position, call ``setOperationalState(LIFT, STALL)`` to indicate that movement is complete. + +getLiftPercentage +^^^^^^^^^^^^^^^^^ + +Gets the current lift percentage. + +.. code-block:: arduino + + uint8_t getLiftPercentage(); + +This function will return the current lift percentage (0-100). + +Tilt Position Control +********************* + +setTiltPosition +^^^^^^^^^^^^^^^ + +Sets the window covering tilt position. Note that tilt is a rotation, not a linear measurement. This method converts the absolute position to percentage using the installed limits. + +.. code-block:: arduino + + bool setTiltPosition(uint16_t tiltPosition); + +* ``tiltPosition`` - Tilt position value (absolute value for conversion, not a physical unit) + +This function will return ``true`` if successful, ``false`` otherwise. + +getTiltPosition +^^^^^^^^^^^^^^^ + +Gets the current tilt position. Note that tilt is a rotation, not a linear measurement. + +.. code-block:: arduino + + uint16_t getTiltPosition(); + +This function will return the current tilt position (absolute value for conversion, not a physical unit). + +setTiltPercentage +^^^^^^^^^^^^^^^^^ + +Sets the window covering tilt position as a percentage. This method updates the ``CurrentPositionTiltPercent100ths`` attribute, which reflects the device's actual position. The ``TargetPositionTiltPercent100ths`` attribute is set by Matter commands/apps when a new target is requested. + +.. code-block:: arduino + + bool setTiltPercentage(uint8_t tiltPercent); + +* ``tiltPercent`` - Tilt percentage (0-100, where 0 is fully closed, 100 is fully open) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** When the device reaches the target position, call ``setOperationalState(TILT, STALL)`` to indicate that movement is complete. + +getTiltPercentage +^^^^^^^^^^^^^^^^^ + +Gets the current tilt percentage. + +.. code-block:: arduino + + uint8_t getTiltPercentage(); + +This function will return the current tilt percentage (0-100). + +Window Covering Type +******************** + +setCoveringType +^^^^^^^^^^^^^^^ + +Sets the window covering type. + +.. code-block:: arduino + + bool setCoveringType(WindowCoveringType_t coveringType); + +* ``coveringType`` - Window covering type (see Window Covering Types enum) + +This function will return ``true`` if successful, ``false`` otherwise. + +getCoveringType +^^^^^^^^^^^^^^^ + +Gets the current window covering type. + +.. code-block:: arduino + + WindowCoveringType_t getCoveringType(); + +This function will return the current window covering type. + +Installed Limit Control +*********************** + +setInstalledOpenLimitLift +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the installed open limit for lift (centimeters). This defines the physical position when the window covering is fully open. + +.. code-block:: arduino + + bool setInstalledOpenLimitLift(uint16_t openLimit); + +* ``openLimit`` - Open limit position (centimeters) + +This function will return ``true`` if successful, ``false`` otherwise. + +getInstalledOpenLimitLift +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the installed open limit for lift. + +.. code-block:: arduino + + uint16_t getInstalledOpenLimitLift(); + +This function will return the installed open limit for lift (centimeters). + +setInstalledClosedLimitLift +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the installed closed limit for lift (centimeters). This defines the physical position when the window covering is fully closed. + +.. code-block:: arduino + + bool setInstalledClosedLimitLift(uint16_t closedLimit); + +* ``closedLimit`` - Closed limit position (centimeters) + +This function will return ``true`` if successful, ``false`` otherwise. + +getInstalledClosedLimitLift +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the installed closed limit for lift. + +.. code-block:: arduino + + uint16_t getInstalledClosedLimitLift(); + +This function will return the installed closed limit for lift (centimeters). + +setInstalledOpenLimitTilt +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the installed open limit for tilt (absolute value for conversion, not a physical unit). This is used for converting between absolute position and percentage. + +.. code-block:: arduino + + bool setInstalledOpenLimitTilt(uint16_t openLimit); + +* ``openLimit`` - Open limit absolute value + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Tilt is a rotation, not a linear measurement. These limits are used for position conversion only. + +getInstalledOpenLimitTilt +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the installed open limit for tilt. + +.. code-block:: arduino + + uint16_t getInstalledOpenLimitTilt(); + +This function will return the installed open limit for tilt (absolute value). + +setInstalledClosedLimitTilt +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the installed closed limit for tilt (absolute value for conversion, not a physical unit). This is used for converting between absolute position and percentage. + +.. code-block:: arduino + + bool setInstalledClosedLimitTilt(uint16_t closedLimit); + +* ``closedLimit`` - Closed limit absolute value + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Tilt is a rotation, not a linear measurement. These limits are used for position conversion only. + +getInstalledClosedLimitTilt +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the installed closed limit for tilt. + +.. code-block:: arduino + + uint16_t getInstalledClosedLimitTilt(); + +This function will return the installed closed limit for tilt (absolute value). + +Target Position Control +*********************** + +setTargetLiftPercent100ths +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the target lift position in percent100ths (0-10000, where 0 is fully closed, 10000 is fully open). + +.. code-block:: arduino + + bool setTargetLiftPercent100ths(uint16_t liftPercent100ths); + +* ``liftPercent100ths`` - Target lift position in percent100ths (0-10000) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** This sets the target position that the device should move towards. The actual position should be updated using ``setLiftPercentage()``. + +getTargetLiftPercent100ths +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current target lift position in percent100ths. + +.. code-block:: arduino + + uint16_t getTargetLiftPercent100ths(); + +This function will return the current target lift position in percent100ths (0-10000). + +setTargetTiltPercent100ths +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the target tilt position in percent100ths (0-10000, where 0 is fully closed, 10000 is fully open). + +.. code-block:: arduino + + bool setTargetTiltPercent100ths(uint16_t tiltPercent100ths); + +* ``tiltPercent100ths`` - Target tilt position in percent100ths (0-10000) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** This sets the target position that the device should move towards. The actual position should be updated using ``setTiltPercentage()``. + +getTargetTiltPercent100ths +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the current target tilt position in percent100ths. + +.. code-block:: arduino + + uint16_t getTargetTiltPercent100ths(); + +This function will return the current target tilt position in percent100ths (0-10000). + +Operational Status Control +************************** + +setOperationalStatus +^^^^^^^^^^^^^^^^^^^^ + +Sets the full operational status bitmap. + +.. code-block:: arduino + + bool setOperationalStatus(uint8_t operationalStatus); + +* ``operationalStatus`` - Full operational status bitmap value + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** It is recommended to use ``setOperationalState()`` to set individual field states instead of setting the full bitmap directly. + +getOperationalStatus +^^^^^^^^^^^^^^^^^^^^ + +Gets the full operational status bitmap. + +.. code-block:: arduino + + uint8_t getOperationalStatus(); + +This function will return the current operational status bitmap value. + +setOperationalState +^^^^^^^^^^^^^^^^^^^ + +Sets the operational state for a specific field (LIFT or TILT). The GLOBAL field is automatically updated based on priority (LIFT > TILT). + +.. code-block:: arduino + + bool setOperationalState(OperationalStatusField_t field, OperationalState_t state); + +* ``field`` - Field to set (``LIFT`` or ``TILT``). ``GLOBAL`` cannot be set directly. +* ``state`` - Operational state (``STALL``, ``MOVING_UP_OR_OPEN``, or ``MOVING_DOWN_OR_CLOSE``) + +This function will return ``true`` if successful, ``false`` otherwise. + +**Note:** Only ``LIFT`` and ``TILT`` fields can be set directly. The ``GLOBAL`` field is automatically updated based on the active field (LIFT has priority over TILT). + +getOperationalState +^^^^^^^^^^^^^^^^^^^ + +Gets the operational state for a specific field. + +.. code-block:: arduino + + OperationalState_t getOperationalState(OperationalStatusField_t field); + +* ``field`` - Field to get (``GLOBAL``, ``LIFT``, or ``TILT``) + +This function will return the operational state for the specified field (``STALL``, ``MOVING_UP_OR_OPEN``, or ``MOVING_DOWN_OR_CLOSE``). + +Event Handling +************** + +The ``MatterWindowCovering`` class automatically detects Matter commands and calls the appropriate callbacks when registered. There are two types of callbacks: + +**Target Position Callbacks** (triggered when ``TargetPosition`` attributes change): +* ``onOpen()`` - called when ``UpOrOpen`` command is received (sets target to 0% = fully open) +* ``onClose()`` - called when ``DownOrClose`` command is received (sets target to 100% = fully closed) +* ``onStop()`` - called when ``StopMotion`` command is received (sets target to current position) +* ``onGoToLiftPercentage()`` - called when ``TargetPositionLiftPercent100ths`` changes (from any command, ``setTargetLiftPercent100ths()``, or direct attribute write) +* ``onGoToTiltPercentage()`` - called when ``TargetPositionTiltPercent100ths`` changes (from any command, ``setTargetTiltPercent100ths()``, or direct attribute write) + +**Current Position Callback** (triggered when ``CurrentPosition`` attributes change): +* ``onChange()`` - called when ``CurrentPositionLiftPercent100ths`` or ``CurrentPositionTiltPercent100ths`` change (after ``setLiftPercentage()``/``setTiltPercentage()`` are called or when a Matter controller updates these attributes directly) + +**Important:** ``onChange()`` is **not** automatically called when Matter commands are executed. Commands modify ``TargetPosition``, not ``CurrentPosition``. To trigger ``onChange()``, your ``onGoToLiftPercentage()`` or ``onGoToTiltPercentage()`` callback must call ``setLiftPercentage()`` or ``setTiltPercentage()`` when the physical device actually moves. + +**Note:** All callbacks are optional. If a specific callback is not registered, only the generic ``onGoToLiftPercentage()`` or ``onGoToTiltPercentage()`` callbacks will be called (if registered). + +onOpen +^^^^^^ + +Sets a callback function to be called when the ``UpOrOpen`` command is received from a Matter controller. This command sets the target position to 0% (fully open). + +.. code-block:: arduino + + void onOpen(EndPointOpenCB onChangeCB); + +* ``onChangeCB`` - Function to call when ``UpOrOpen`` command is received + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(); + +onClose +^^^^^^^ + +Sets a callback function to be called when the ``DownOrClose`` command is received from a Matter controller. This command sets the target position to 100% (fully closed). + +.. code-block:: arduino + + void onClose(EndPointCloseCB onChangeCB); + +* ``onChangeCB`` - Function to call when ``DownOrClose`` command is received + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(); + +onGoToLiftPercentage +^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when ``TargetPositionLiftPercent100ths`` changes. This is triggered by: +* Matter commands: ``UpOrOpen``, ``DownOrClose``, ``StopMotion``, ``GoToLiftPercentage`` +* Calling ``setTargetLiftPercent100ths()`` +* Direct attribute writes to ``TargetPositionLiftPercent100ths`` + +This callback is always called when the target lift position changes, regardless of which command or method was used to change it. + +**Note:** This callback receives the **target** position. To update the **current** position (which triggers ``onChange()``), call ``setLiftPercentage()`` when the physical device actually moves. + +.. code-block:: arduino + + void onGoToLiftPercentage(EndPointLiftCB onChangeCB); + +* ``onChangeCB`` - Function to call when target lift percentage changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(uint8_t liftPercent); + +* ``liftPercent`` - Target lift percentage (0-100, where 0 is fully closed, 100 is fully open) + +onGoToTiltPercentage +^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when ``TargetPositionTiltPercent100ths`` changes. This is triggered by: +* Matter commands: ``UpOrOpen``, ``DownOrClose``, ``StopMotion``, ``GoToTiltPercentage`` +* Calling ``setTargetTiltPercent100ths()`` +* Direct attribute writes to ``TargetPositionTiltPercent100ths`` + +This callback is always called when the target tilt position changes, regardless of which command or method was used to change it. + +**Note:** This callback receives the **target** position. To update the **current** position (which triggers ``onChange()``), call ``setTiltPercentage()`` when the physical device actually moves. + +.. code-block:: arduino + + void onGoToTiltPercentage(EndPointTiltCB onChangeCB); + +* ``onChangeCB`` - Function to call when target tilt percentage changes + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(uint8_t tiltPercent); + +* ``tiltPercent`` - Target tilt percentage (0-100, where 0 is fully closed, 100 is fully open) + +onStop +^^^^^^ + +Sets a callback function to be called when the ``StopMotion`` command is received from a Matter controller. This command sets the target position to the current position, effectively stopping any movement. + +.. code-block:: arduino + + void onStop(EndPointStopCB onChangeCB); + +* ``onChangeCB`` - Function to call when ``StopMotion`` command is received + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(); + +onChange +^^^^^^^^ + +Sets a callback function to be called when ``CurrentPositionLiftPercent100ths`` or ``CurrentPositionTiltPercent100ths`` attributes change. This is different from ``onGoToLiftPercentage()`` and ``onGoToTiltPercentage()``, which are called when ``TargetPosition`` attributes change. + +**When ``onChange()`` is called:** +* When ``CurrentPositionLiftPercent100ths`` changes (after ``setLiftPercentage()`` is called or when a Matter controller updates this attribute directly) +* When ``CurrentPositionTiltPercent100ths`` changes (after ``setTiltPercentage()`` is called or when a Matter controller updates this attribute directly) + +**Important:** ``onChange()`` is **not** automatically called when Matter commands are executed. Commands modify ``TargetPosition`` attributes, which trigger ``onGoToLiftPercentage()`` or ``onGoToTiltPercentage()`` callbacks instead. To trigger ``onChange()`` after a command, your ``onGoToLiftPercentage()`` or ``onGoToTiltPercentage()`` callback must call ``setLiftPercentage()`` or ``setTiltPercentage()`` to update the ``CurrentPosition`` attributes when the physical device actually moves. + +.. code-block:: arduino + + void onChange(EndPointCB onChangeCB); + +* ``onChangeCB`` - Function to call when current position attributes change + +The callback signature is: + +.. code-block:: arduino + + bool onChangeCallback(uint8_t liftPercent, uint8_t tiltPercent); + +* ``liftPercent`` - Current lift percentage (0-100) +* ``tiltPercent`` - Current tilt percentage (0-100) + +updateAccessory +^^^^^^^^^^^^^^^ + +Updates the state of the window covering using the current Matter internal state. + +.. code-block:: arduino + + void updateAccessory(); + +This function will call the registered callback with the current state. + +Example +------- + +Window Covering +*************** + +.. literalinclude:: ../../../libraries/Matter/examples/MatterWindowCovering/MatterWindowCovering.ino + :language: arduino diff --git a/docs/en/matter/matter.rst b/docs/en/matter/matter.rst new file mode 100644 index 00000000000..1ea5b3573b7 --- /dev/null +++ b/docs/en/matter/matter.rst @@ -0,0 +1,301 @@ +###### +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. + +Building and Flashing Matter Examples +-------------------------------------- + +Before uploading any Matter example sketch, it is necessary to configure the Arduino IDE with the following settings: + +1. **Partition Scheme**: Select **"Huge APP (3 MB No OTA / 1 MB SPIFFS)"** from **Tools > Partition Scheme** menu. + + .. figure:: ../../_static/matter_partition_scheme.png + :align: center + :alt: "Partition Scheme: Huge APP (3 MB No OTA / 1 MB SPIFFS)" Arduino IDE menu option + :figclass: align-center + +2. **Erase Flash**: Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. + + .. figure:: ../../_static/matter_erase_flash.png + :align: center + :alt: "Erase All Flash Before Sketch Upload: Enabled" Arduino IDE menu option + :figclass: align-center + +These settings are required for the following reasons: + +* **Partition Scheme**: Matter firmware requires a large application partition (3 MB) to accommodate the Matter stack and application code. +* **Erase Flash**: Erasing flash is necessary to remove any leftover Wi-Fi or Matter configuration from the NVS (Non-Volatile Storage) partition. Without erasing, previous network credentials, Matter fabric information, or device commissioning data may interfere with the new firmware, causing commissioning failures or connectivity issues. + +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 +* ``isWiFiConnected()``: Checks Wi-Fi connection status +* ``isThreadConnected()``: Checks Thread connection status +* ``isDeviceConnected()``: Checks overall device connectivity +* ``isWiFiStationEnabled()``: Checks if Wi-Fi Station mode is supported and enabled +* ``isWiFiAccessPointEnabled()``: Checks if Wi-Fi AP mode is supported and enabled +* ``isThreadEnabled()``: Checks if Thread network is supported and enabled +* ``isBLECommissioningEnabled()``: Checks if BLE commissioning is supported and enabled +* ``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) +* ``MatterWindowCovering``: Window covering with lift and tilt control (blinds, shades) + +.. toctree:: + :maxdepth: 1 + :glob: + + ep_* + +Matter Examples +--------------- + +The Matter library includes a comprehensive set of examples demonstrating various device types and use cases. All examples are available in the `ESP Arduino GitHub repository `_. + +**Basic Examples:** + +* **Matter Minimum** - The smallest code required to create a Matter-compatible device. Ideal starting point for understanding Matter basics. `View Matter Minimum code on GitHub `_ +* **Matter Status** - Demonstrates how to check enabled Matter features and connectivity status. Implements a basic on/off light and periodically reports capability and connection status. `View Matter Status code on GitHub `_ +* **Matter Events** - Shows how to monitor and handle Matter events. Provides a comprehensive view of all Matter events during device operation. `View Matter Events code on GitHub `_ +* **Matter Commission Test** - Tests Matter commissioning functionality with automatic decommissioning after a 30-second delay for continuous testing cycles. `View Matter Commission Test code on GitHub `_ + +**Lighting Examples:** + +* **Matter On/Off Light** - Creates a Matter-compatible on/off light device with commissioning, device control via smart home ecosystems, and manual control using a physical button with state persistence. `View Matter On/Off Light code on GitHub `_ +* **Matter Dimmable Light** - Creates a Matter-compatible dimmable light device with brightness control. `View Matter Dimmable Light code on GitHub `_ +* **Matter Color Temperature Light** - Creates a Matter-compatible color temperature light device with adjustable color temperature control. `View Matter Color Temperature Light code on GitHub `_ +* **Matter Color Light** - Creates a Matter-compatible color light device with RGB color control (HSV color model). `View Matter Color Light code on GitHub `_ +* **Matter Enhanced Color Light** - Creates a Matter-compatible enhanced color light with color temperature and brightness control. `View Matter Enhanced Color Light code on GitHub `_ +* **Matter Composed Lights** - Creates a Matter node with multiple light endpoints (On/Off Light, Dimmable Light, and Color Light) in a single node. `View Matter Composed Lights code on GitHub `_ +* **Matter On Identify** - Implements the Matter Identify cluster callback for an on/off light device, making the LED blink when the device is identified from a Matter app. `View Matter On Identify code on GitHub `_ + +**Sensor Examples:** + +* **Matter Temperature Sensor** - Creates a Matter-compatible temperature sensor device with sensor data reporting to smart home ecosystems. `View Matter Temperature Sensor code on GitHub `_ +* **Matter Humidity Sensor** - Creates a Matter-compatible humidity sensor device with sensor data reporting. `View Matter Humidity Sensor code on GitHub `_ +* **Matter Pressure Sensor** - Creates a Matter-compatible pressure sensor device with automatic simulation of pressure readings. `View Matter Pressure Sensor code on GitHub `_ +* **Matter Contact Sensor** - Creates a Matter-compatible contact sensor device (open/closed state). `View Matter Contact Sensor code on GitHub `_ +* **Matter Occupancy Sensor** - Creates a Matter-compatible occupancy sensor device with automatic simulation of occupancy state changes. `View Matter Occupancy Sensor code on GitHub `_ +* **Matter Occupancy Sensor with HoldTime** - Creates a Matter-compatible occupancy sensor device with HoldTime functionality, automatic simulation of occupancy state changes, HoldTime configuration with persistence across reboots, and HoldTime change callback for real-time updates from Matter controllers. `View Matter Occupancy Sensor with HoldTime code on GitHub `_ +* **Matter Water Leak Detector** - Creates a Matter-compatible water leak detector device with automatic simulation of water leak detection state changes. `View Matter Water Leak Detector code on GitHub `_ +* **Matter Water Freeze Detector** - Creates a Matter-compatible water freeze detector device with automatic simulation of water freeze detection state changes. `View Matter Water Freeze Detector code on GitHub `_ +* **Matter Rain Sensor** - Creates a Matter-compatible rain sensor device with automatic simulation of rain detection state changes. `View Matter Rain Sensor code on GitHub `_ + +**Control Examples:** + +* **Matter Fan** - Creates a Matter-compatible fan device with speed and mode control. `View Matter Fan code on GitHub `_ +* **Matter Thermostat** - Creates a Matter-compatible thermostat device with temperature setpoint management and simulated heating/cooling systems with automatic temperature regulation. `View Matter Thermostat code on GitHub `_ +* **Matter Temperature Controlled Cabinet** - Creates a Matter-compatible temperature controlled cabinet device with precise temperature setpoint control with min/max limits (temperature_number mode). `View Matter Temperature Controlled Cabinet code on GitHub `_ +* **Matter Temperature Controlled Cabinet Levels** - Creates a Matter-compatible temperature controlled cabinet device using predefined temperature levels (temperature_level mode). `View Matter Temperature Controlled Cabinet Levels code on GitHub `_ +* **Matter On/Off Plugin** - Creates a Matter-compatible on/off plugin unit (power relay) device with state persistence for power control applications. `View Matter On/Off Plugin code on GitHub `_ +* **Matter Dimmable Plugin** - Creates a Matter-compatible dimmable plugin unit (power outlet with level control) device with state persistence for dimmable power control applications. `View Matter Dimmable Plugin code on GitHub `_ +* **Matter Smart Button** - Creates a Matter-compatible smart button (generic switch) device that sends button click events to smart home ecosystems and triggers automations. `View Matter Smart Button code on GitHub `_ +* **Matter Window Covering** - Creates a Matter-compatible window covering device with lift and tilt control (blinds, shades) with manual control using a physical button. `View Matter Window Covering code on GitHub `_ +* **Matter Simple Blinds** - A minimal example that only controls lift percentage using a single onGoToLiftPercentage() callback. `View Matter Simple Blinds code on GitHub `_ + +**Advanced Examples:** + +* **Matter Lambda Single Callback Many Endpoints** - Demonstrates how to create multiple Matter endpoints in a single node using a shared lambda function callback with capture for efficient callback handling. `View Matter Lambda Single Callback Many Endpoints code on GitHub `_ + +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_carbon_dioxide_sensor.rst b/docs/en/zigbee/ep_carbon_dioxide_sensor.rst index 6f219a16fd0..d595fda8e1f 100644 --- a/docs/en/zigbee/ep_carbon_dioxide_sensor.rst +++ b/docs/en/zigbee/ep_carbon_dioxide_sensor.rst @@ -54,6 +54,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the carbon dioxide sensor in ppm. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(float defaultValue); + +* ``defaultValue`` - Default CO2 concentration value in ppm + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ 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_flow_sensor.rst b/docs/en/zigbee/ep_flow_sensor.rst index 9423f321a5d..95c3b5c72c4 100644 --- a/docs/en/zigbee/ep_flow_sensor.rst +++ b/docs/en/zigbee/ep_flow_sensor.rst @@ -70,6 +70,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the flow sensor in 0.1 m³/h. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(float defaultValue); + +* ``defaultValue`` - Default flow rate value in 0.1 m³/h + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ diff --git a/docs/en/zigbee/ep_illuminance_sensor.rst b/docs/en/zigbee/ep_illuminance_sensor.rst index 1e627f7dfe9..73b676e3f52 100644 --- a/docs/en/zigbee/ep_illuminance_sensor.rst +++ b/docs/en/zigbee/ep_illuminance_sensor.rst @@ -60,6 +60,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the illuminance sensor. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(uint16_t defaultValue); + +* ``defaultValue`` - Default illuminance value in lux + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ diff --git a/docs/en/zigbee/ep_pm25_sensor.rst b/docs/en/zigbee/ep_pm25_sensor.rst index 2f1432f8224..feac3c54e66 100644 --- a/docs/en/zigbee/ep_pm25_sensor.rst +++ b/docs/en/zigbee/ep_pm25_sensor.rst @@ -60,6 +60,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the PM2.5 sensor in 0.1 μg/m³. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(float defaultValue); + +* ``defaultValue`` - Default PM2.5 concentration value in 0.1 μg/m³ + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ diff --git a/docs/en/zigbee/ep_pressure_sensor.rst b/docs/en/zigbee/ep_pressure_sensor.rst index 5d857bb163e..b8a5fbeb18d 100644 --- a/docs/en/zigbee/ep_pressure_sensor.rst +++ b/docs/en/zigbee/ep_pressure_sensor.rst @@ -60,6 +60,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the pressure sensor in 1 hPa. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(int16_t defaultValue); + +* ``defaultValue`` - Default pressure value in hPa + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ diff --git a/docs/en/zigbee/ep_temperature_sensor.rst b/docs/en/zigbee/ep_temperature_sensor.rst index 85c2147f1a1..f14082d59f0 100644 --- a/docs/en/zigbee/ep_temperature_sensor.rst +++ b/docs/en/zigbee/ep_temperature_sensor.rst @@ -60,6 +60,21 @@ Sets the minimum and maximum temperature values for the sensor. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the temperature sensor in 0.01°C resolution. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(float defaultValue); + +* ``defaultValue`` - Default temperature value in degrees Celsius + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ 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/ep_wind_speed_sensor.rst b/docs/en/zigbee/ep_wind_speed_sensor.rst index 67c4958e37c..7b294674e89 100644 --- a/docs/en/zigbee/ep_wind_speed_sensor.rst +++ b/docs/en/zigbee/ep_wind_speed_sensor.rst @@ -70,6 +70,21 @@ Sets the minimum and maximum measurement values. This function will return ``true`` if successful, ``false`` otherwise. +setDefaultValue +^^^^^^^^^^^^^^^ + +Sets the default (initial) value for the wind speed sensor in 0.01 m/s. This value will be used as the initial measured value when the device is in factory reset mode and before the sensor provides actual readings. + +.. code-block:: arduino + + bool setDefaultValue(float defaultValue); + +* ``defaultValue`` - Default wind speed value in 0.01 m/s + +**Important:** Must be called before adding the EP to Zigbee class. Only effective when the device is in factory reset mode (before commissioning/joining a network). + +This function will return ``true`` if successful, ``false`` otherwise. + setTolerance ^^^^^^^^^^^^ diff --git a/docs/requirements.txt b/docs/requirements.txt index ef2ab88cb65..09e458b5caf 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,7 +1,7 @@ -sphinx==4.5.0 -esp-docs>=1.4.0 +sphinx==7.1.2 +esp-docs==2.1.1 sphinx-copybutton==0.5.0 -sphinx-tabs==3.2.0 -numpydoc==1.5.0 +sphinx-tabs==3.4.7 +numpydoc==1.10.0 standard-imghdr==3.13.0 Sphinx-Substitution-Extensions==2022.2.16 diff --git a/idf_component.yml b/idf_component.yml index 9cbe8aac52c..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.7 + 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.7" + 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: @@ -132,4 +134,4 @@ dependencies: 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 934789a52bf..4dba9351ebf 100644 --- a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino +++ b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino @@ -38,9 +38,6 @@ void setup() { // Hostname defaults to esp3232-[MAC] // ArduinoOTA.setHostname("myesp32"); - // No authentication by default - // ArduinoOTA.setPassword("admin"); - // Password can be set with plain text (will be hashed internally) // The authentication uses PBKDF2-HMAC-SHA256 with 10,000 iterations // ArduinoOTA.setPassword("admin"); 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/examples/SignedOTA/README.md b/libraries/ArduinoOTA/examples/SignedOTA/README.md new file mode 100644 index 00000000000..bf5f12b6d67 --- /dev/null +++ b/libraries/ArduinoOTA/examples/SignedOTA/README.md @@ -0,0 +1,323 @@ +# SignedOTA - Secure OTA Updates with Signature Verification + +This example demonstrates how to perform secure OTA updates with cryptographic signature verification using the ArduinoOTA library. + +## Overview + +**SignedOTA** adds an extra layer of security to Arduino OTA updates by requiring all firmware to be cryptographically signed with your private key. This protects against: + +- ✅ Unauthorized firmware updates +- ✅ Man-in-the-middle attacks +- ✅ Compromised networks +- ✅ Firmware tampering +- ✅ Supply chain attacks + +Even if an attacker gains access to your network, they **cannot** install unsigned firmware on your devices. + +## Features + +- **RSA & ECDSA Support**: RSA-2048/3072/4096 and ECDSA-P256/P384 +- **Multiple Hash Algorithms**: SHA-256, SHA-384, SHA-512 +- **Arduino IDE Compatible**: Works with standard Arduino OTA workflow +- **Optional Password Protection**: Add password authentication in addition to signature verification +- **Easy Integration**: Just a few lines of code + +## Requirements + +- **ESP32 Arduino Core 3.3.0+** +- **Python 3.6+** with `cryptography` library + +- **OTA-capable partition scheme** (e.g., "Minimal SPIFFS (1.9MB APP with OTA)") + + +## Quick Start Guide + +### 1. Generate Cryptographic Keys + +```bash +# Navigate to Arduino ESP32 tools directory +cd /tools + +# Install Python dependencies +pip install cryptography + +# Generate RSA-2048 key pair (recommended) +python bin_signing.py --generate-key rsa-2048 --out private_key.pem + +# Extract public key +python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +``` + +**⚠️ IMPORTANT: Keep `private_key.pem` secure! Anyone with this key can sign firmware for your devices.** + +### 2. Setup the Example + +1. Copy `public_key.h` (generated in step 1) to this sketch directory +2. Open `SignedOTA.ino` in Arduino IDE + +3. Configure WiFi credentials: + ```cpp + const char *ssid = "YourWiFiSSID"; + const char *password = "YourWiFiPassword"; + ``` + + +4. Select appropriate partition scheme: + - **Tools → Partition Scheme → "Minimal SPIFFS (1.9MB APP with OTA)"** + + +### 3. Upload Initial Firmware + +1. Connect your ESP32 via USB +2. Upload the sketch normally +3. Open Serial Monitor (115200 baud) +4. Note the device IP address + +### 4. Build & Sign Firmware for OTA Update Example + +**Option A: Using Arduino IDE** + +```bash +# Export compiled binary +# In Arduino IDE: Sketch → Export Compiled Binary + +# Sign the firmware +cd /tools +python bin_signing.py \ + --bin /path/to/SignedOTA.ino.bin \ + --key private_key.pem \ + --out firmware_signed.bin +``` + +**Option B: Using arduino-cli** + +```bash +# Compile and export +arduino-cli compile --fqbn esp32:esp32:esp32 --export-binaries SignedOTA + +# Sign the firmware +cd /tools +python bin_signing.py \ + --bin build/esp32.esp32.esp32/SignedOTA.ino.bin \ + --key private_key.pem \ + --out firmware_signed.bin +``` + +### 5. Upload Signed Firmware via OTA + +Upload the signed firmware using `espota.py`: + +```bash +python /tools/espota.py -i -f firmware_signed.bin +``` + +The device will automatically: +1. Receive the signed firmware (firmware + signature) +2. Hash only the firmware portion +3. Verify the signature +4. Install if valid, reject if invalid + +**Note**: You can also use the Update library's `Signed_OTA_Update` example for HTTP-based OTA updates. + +## Configuration Options + +### Hash Algorithms + +Choose one in `SignedOTA.ino`: + +```cpp +#define USE_SHA256 // Default, fastest +// #define USE_SHA384 +// #define USE_SHA512 +``` + +**Must match** the `--hash` parameter when signing: + +```bash +python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha256 +``` + +### Signature Algorithms + +Choose one in `SignedOTA.ino`: + +```cpp +#define USE_RSA // For RSA keys +// #define USE_ECDSA // For ECDSA keys +``` + +### Optional Password Protection + +Add password authentication **in addition to** signature verification: + +```cpp +const char *ota_password = "yourpassword"; // Set password +// const char *ota_password = nullptr; // Disable password +``` + +## How It Works + +``` +┌─────────────────┐ +│ Build Firmware │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Sign Firmware │ ← Uses your private key +│ (bin_signing) │ +└────────┬────────┘ + │ + ▼ +┌─────────────────────────┐ +│ firmware_signed.bin │ +│ [firmware][signature] │ +└────────┬────────────────┘ + │ + ▼ OTA Upload +┌─────────────────────────┐ +│ ESP32 Device │ +│ ┌──────────────────┐ │ +│ │ Verify Signature │ │ ← Uses your public key +│ │ ✓ or ✗ │ │ +│ └──────────────────┘ │ +│ │ │ +│ ✓ Valid? │ +│ ├─ Yes: Install │ +│ └─ No: Reject │ +└─────────────────────────┘ +``` + +## Troubleshooting + +### "Begin Failed" Error + +**Cause**: Signature verification setup failed, or partition scheme issue + +**Solutions**: + +1. Check partition scheme (use "Minimal SPIFFS (1.9MB APP with OTA)") +2. Verify `public_key.h` is in the sketch directory +3. Check hash and signature algorithm match your key type + + +### "End Failed" Error + +**Cause**: Signature verification failed + +**Solutions**: +1. Ensure firmware was signed with the **correct private key** +2. Verify hash algorithm matches (SHA-256, SHA-384, SHA-512) +3. Check firmware wasn't corrupted during signing/transfer +4. Confirm you signed the **correct** `.bin` file + +### "Receive Failed" Error + +**Cause**: Network timeout or connection issue + +**Solutions**: +1. Check Wi-Fi signal strength +2. Ensure device is reachable on the network +3. Try increasing timeout: `ArduinoOTA.setTimeout(5000)` + +### Upload Fails + +**Issue**: OTA upload fails or times out + +**Solutions**: +1. Verify device is on the same network +2. Check firewall settings aren't blocking port 3232 +3. Ensure Wi-Fi signal strength is adequate +4. If using password protection, ensure the password is correct +5. Try: `python /tools/espota.py -i -f firmware_signed.bin -d` + +## Security Considerations + +### Best Practices + +✅ **Keep private key secure**: Never commit to git, store encrypted +✅ **Use strong keys**: RSA-2048+ or ECDSA-P256+ +✅ **Use HTTPS when possible**: For additional transport security +✅ **Add password authentication**: Extra layer of protection +✅ **Rotate keys periodically**: Generate new keys every 1-2 years + +### What This Protects Against + +- ✅ Unsigned firmware installation +- ✅ Firmware signed with wrong key +- ✅ Tampered/corrupted firmware +- ✅ Network-based attacks (when combined with password) + +### What This Does NOT Protect Against + + +- ❌ Physical access (USB flashing still works) +- ❌ Downgrade attacks (no version checking by default) +- ❌ Replay attacks (no timestamp/nonce by default) +- ❌ Key compromise (if private key is stolen) + + +### Additional Security + +For production deployments, consider: + +1. **Add version checking** to prevent downgrades +2. **Add timestamp validation** to prevent replay attacks +3. **Use secure boot** for additional protection +4. **Store keys in HSM** or secure key management system +5. **Implement key rotation** mechanism + +## Advanced Usage + +### Using ECDSA Instead of RSA + +ECDSA keys are smaller and faster: + +```bash +# Generate ECDSA-P256 key +python bin_signing.py --generate-key ecdsa-p256 --out private_key.pem +python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +``` + +In `SignedOTA.ino`: + +```cpp +#define USE_SHA256 +#define USE_ECDSA // Instead of USE_RSA +``` + +### Using SHA-384 or SHA-512 + +For higher security: + +```bash +# Sign with SHA-384 +python bin_signing.py --bin firmware.bin --key private.pem --out signed.bin --hash sha384 +``` + +In `SignedOTA.ino`: + +```cpp +#define USE_SHA384 // Instead of USE_SHA256 +#define USE_RSA +``` + +### Custom Partition Label + +To update a specific partition: + +```cpp +ArduinoOTA.setPartitionLabel("my_partition"); +``` + +## Support + +For issues and questions: + +- Update Library README: `libraries/Update/README.md` +- ESP32 Arduino Core: https://github.com/espressif/arduino-esp32 +- Forum: https://github.com/espressif/arduino-esp32/discussions + +## License + +This library is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0. diff --git a/libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino b/libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino new file mode 100644 index 00000000000..ec23489ee4b --- /dev/null +++ b/libraries/ArduinoOTA/examples/SignedOTA/SignedOTA.ino @@ -0,0 +1,204 @@ +/* + * SignedOTA Example - Secure OTA Updates with Signature Verification + * + * This example demonstrates how to perform OTA updates with cryptographic + * signature verification using ArduinoOTA library. + * + * IMPORTANT: This example requires firmware to be signed with bin_signing.py + * + * NOTE: Signature verification support is enabled via the build_opt.h file + * in this directory. + * + * Setup: + * 1. Generate keys: + * python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem + * python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + * + * 2. Copy public_key.h to this sketch directory + * + * 3. Configure WiFi credentials below + * + * 4. Upload this sketch to your device + * + * 5. Build your firmware and sign it: + * arduino-cli compile --fqbn esp32:esp32:esp32 --export-binaries SignedOTA + * python /tools/bin_signing.py --bin build/.bin --key private_key.pem --out firmware_signed.bin + * + * 6. Upload signed firmware using espota.py or Arduino IDE (after modifying espota.py to handle signed binaries) + * python /tools/espota.py -i -f firmware_signed.bin + * + * For more information, see the Update library's Signed_OTA_Update example + * and README.md in the Update library folder. + * + * Created by lucasssvaz + */ + +#include +#include +#include +#include + +// Include your public key (generated with bin_signing.py) +#include "public_key.h" + +// ==================== CONFIGURATION ==================== + +// WiFi credentials +const char *ssid = ".........."; +const char *password = ".........."; + +// Optional: Set a password for OTA authentication +// This is in ADDITION to signature verification +// ArduinoOTA password protects the OTA connection +// Signature verification ensures firmware authenticity +const char *ota_password = nullptr; // Set to nullptr to disable, or "yourpassword" to enable + +// Choose hash algorithm (must match what you use with bin_signing.py --hash) +// Uncomment ONE of these: +#define USE_SHA256 // Default, recommended +// #define USE_SHA384 +// #define USE_SHA512 + +// Choose signature algorithm (must match your key type) +// Uncomment ONE of these: +#define USE_RSA // Recommended (works with rsa-2048, rsa-3072, rsa-4096) +// #define USE_ECDSA // Works with ecdsa-p256, ecdsa-p384 + +// ======================================================= + +uint32_t last_ota_time = 0; + +void setup() { + Serial.begin(115200); + Serial.println("\n\n================================="); + Serial.println("SignedOTA - Secure OTA Updates"); + Serial.println("=================================\n"); + Serial.println("Booting..."); + + // Connect to WiFi + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.waitForConnectResult() != WL_CONNECTED) { + Serial.println("Connection Failed! Rebooting..."); + delay(5000); + ESP.restart(); + } + + Serial.println("WiFi Connected!"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + // ==================== SIGNATURE VERIFICATION SETUP ==================== + + // Select hash algorithm +#ifdef USE_SHA256 + int hashType = HASH_SHA256; + Serial.println("Using SHA-256 hash"); +#elif defined(USE_SHA384) + int hashType = HASH_SHA384; + Serial.println("Using SHA-384 hash"); +#elif defined(USE_SHA512) + int hashType = HASH_SHA512; + Serial.println("Using SHA-512 hash"); +#else +#error "Please define a hash algorithm (USE_SHA256, USE_SHA384, or USE_SHA512)" +#endif + + // Create verifier object +#ifdef USE_RSA + static UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); + Serial.println("Using RSA signature verification"); +#elif defined(USE_ECDSA) + static UpdaterECDSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); + Serial.println("Using ECDSA signature verification"); +#else +#error "Please define a signature type (USE_RSA or USE_ECDSA)" +#endif + + // Install signature verification BEFORE ArduinoOTA.begin() + ArduinoOTA.setSignature(&sign); + Serial.println("✓ Signature verification enabled"); + + // ======================================================================= + + // Optional: Set hostname + // ArduinoOTA.setHostname("myesp32"); + + // Optional: Set OTA password (in addition to signature verification) + if (ota_password != nullptr) { + ArduinoOTA.setPassword(ota_password); + Serial.println("✓ OTA password protection enabled"); + } + + // Configure OTA callbacks + ArduinoOTA + .onStart([]() { + String type; + if (ArduinoOTA.getCommand() == U_FLASH) { + type = "sketch"; + } else { // U_SPIFFS + type = "filesystem"; + } + Serial.println("\n================================="); + Serial.println("OTA Update Starting: " + type); + Serial.println("================================="); + Serial.println("⚠️ Signature will be verified!"); + }) + .onEnd([]() { + Serial.println("\n================================="); + Serial.println("✅ OTA Update Complete!"); + Serial.println("✅ Signature Verified!"); + Serial.println("================================="); + Serial.println("Rebooting..."); + }) + .onProgress([](unsigned int progress, unsigned int total) { + if (millis() - last_ota_time > 500) { + Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + last_ota_time = millis(); + } + }) + .onError([](ota_error_t error) { + Serial.println("\n================================="); + Serial.println("❌ OTA Update Failed!"); + Serial.println("================================="); + Serial.printf("Error[%u]: ", error); + if (error == OTA_AUTH_ERROR) { + Serial.println("Authentication Failed"); + Serial.println("Check your OTA password"); + } else if (error == OTA_BEGIN_ERROR) { + Serial.println("Begin Failed"); + Serial.println("This could be:"); + Serial.println("- Signature verification setup failed"); + Serial.println("- Not enough space for update"); + Serial.println("- Invalid partition"); + } else if (error == OTA_CONNECT_ERROR) { + Serial.println("Connect Failed"); + } else if (error == OTA_RECEIVE_ERROR) { + Serial.println("Receive Failed"); + } else if (error == OTA_END_ERROR) { + Serial.println("End Failed"); + Serial.println("This could be:"); + Serial.println("- ❌ SIGNATURE VERIFICATION FAILED!"); + Serial.println("- Firmware not signed with correct key"); + Serial.println("- Firmware corrupted during transfer"); + Serial.println("- MD5 checksum mismatch"); + } + Serial.println("================================="); + }); + + // Start ArduinoOTA service + ArduinoOTA.begin(); + + Serial.println("\n================================="); + Serial.println("✓ OTA Server Ready"); + Serial.println("================================="); + Serial.printf("Hostname: %s.local\n", ArduinoOTA.getHostname().c_str()); + Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str()); + Serial.println("Port: 3232"); + Serial.println("\n⚠️ Only signed firmware will be accepted!"); + Serial.println("=================================\n"); +} + +void loop() { + ArduinoOTA.handle(); +} diff --git a/libraries/ArduinoOTA/examples/SignedOTA/build_opt.h b/libraries/ArduinoOTA/examples/SignedOTA/build_opt.h new file mode 100644 index 00000000000..1b328fa2487 --- /dev/null +++ b/libraries/ArduinoOTA/examples/SignedOTA/build_opt.h @@ -0,0 +1 @@ +-DUPDATE_SIGN diff --git a/libraries/ArduinoOTA/examples/SignedOTA/ci.yml b/libraries/ArduinoOTA/examples/SignedOTA/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/ArduinoOTA/examples/SignedOTA/ci.yml @@ -0,0 +1,3 @@ +requires_any: + - CONFIG_SOC_WIFI_SUPPORTED=y + - CONFIG_ESP_WIFI_REMOTE_ENABLED=y diff --git a/libraries/ArduinoOTA/examples/SignedOTA/public_key.h b/libraries/ArduinoOTA/examples/SignedOTA/public_key.h new file mode 100644 index 00000000000..fd820287134 --- /dev/null +++ b/libraries/ArduinoOTA/examples/SignedOTA/public_key.h @@ -0,0 +1,30 @@ +// Public key for OTA signature verification +// Include this in your Arduino sketch + +// ⚠️ THIS IS A TEST KEY - DO NOT USE IN PRODUCTION! +// Generate your own keys using: +// python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem +// python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +// +// Then replace this file with the generated public_key.h + +// Test RSA-2048 Public Key (PEM format) +const uint8_t PUBLIC_KEY[] PROGMEM = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x4d, 0x49, 0x49, 0x42, 0x49, 0x6a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, + 0x41, 0x4f, 0x43, 0x41, 0x51, 0x38, 0x41, 0x4d, 0x49, 0x49, 0x42, 0x43, 0x67, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x73, 0x35, 0x35, 0x66, 0x4f, 0x74, 0x51, + 0x64, 0x69, 0x70, 0x39, 0x58, 0x6f, 0x49, 0x61, 0x6c, 0x52, 0x5a, 0x4b, 0x6c, 0x4a, 0x0a, 0x52, 0x62, 0x55, 0x73, 0x49, 0x30, 0x4c, 0x48, 0x5a, 0x74, 0x2b, + 0x50, 0x58, 0x35, 0x4b, 0x58, 0x43, 0x79, 0x54, 0x64, 0x63, 0x78, 0x71, 0x6c, 0x6f, 0x44, 0x45, 0x2b, 0x63, 0x38, 0x43, 0x6f, 0x32, 0x50, 0x77, 0x37, 0x6f, + 0x66, 0x63, 0x66, 0x30, 0x47, 0x41, 0x38, 0x4a, 0x55, 0x65, 0x6e, 0x6d, 0x45, 0x46, 0x6b, 0x57, 0x6a, 0x50, 0x53, 0x48, 0x4c, 0x55, 0x55, 0x79, 0x44, 0x0a, + 0x63, 0x65, 0x4b, 0x63, 0x2b, 0x71, 0x45, 0x47, 0x54, 0x58, 0x72, 0x59, 0x39, 0x56, 0x6f, 0x4d, 0x38, 0x6f, 0x42, 0x58, 0x39, 0x67, 0x48, 0x41, 0x64, 0x4b, + 0x4f, 0x51, 0x48, 0x33, 0x50, 0x4d, 0x70, 0x4a, 0x69, 0x56, 0x51, 0x71, 0x4e, 0x43, 0x36, 0x37, 0x31, 0x44, 0x37, 0x54, 0x45, 0x76, 0x4e, 0x52, 0x43, 0x67, + 0x6e, 0x4f, 0x41, 0x37, 0x77, 0x62, 0x77, 0x6f, 0x78, 0x4e, 0x0a, 0x63, 0x75, 0x59, 0x30, 0x49, 0x6e, 0x51, 0x4e, 0x30, 0x64, 0x6b, 0x42, 0x43, 0x4f, 0x63, + 0x34, 0x4e, 0x66, 0x31, 0x56, 0x42, 0x76, 0x35, 0x64, 0x71, 0x55, 0x57, 0x41, 0x62, 0x66, 0x43, 0x57, 0x68, 0x5a, 0x37, 0x31, 0x72, 0x4a, 0x56, 0x32, 0x53, + 0x68, 0x79, 0x35, 0x48, 0x42, 0x48, 0x48, 0x52, 0x4e, 0x43, 0x78, 0x4f, 0x67, 0x58, 0x68, 0x4f, 0x6c, 0x66, 0x6c, 0x66, 0x0a, 0x72, 0x49, 0x57, 0x56, 0x71, + 0x66, 0x51, 0x4b, 0x2b, 0x75, 0x54, 0x4d, 0x62, 0x39, 0x4a, 0x4c, 0x51, 0x67, 0x76, 0x4a, 0x66, 0x70, 0x4c, 0x61, 0x65, 0x35, 0x35, 0x61, 0x61, 0x4e, 0x77, + 0x63, 0x72, 0x62, 0x59, 0x38, 0x58, 0x67, 0x53, 0x79, 0x31, 0x64, 0x6c, 0x58, 0x76, 0x4e, 0x37, 0x4d, 0x33, 0x75, 0x4c, 0x52, 0x72, 0x4b, 0x79, 0x61, 0x75, + 0x34, 0x59, 0x0a, 0x39, 0x51, 0x53, 0x71, 0x76, 0x4a, 0x71, 0x67, 0x52, 0x61, 0x36, 0x66, 0x47, 0x51, 0x2f, 0x4d, 0x41, 0x63, 0x6c, 0x48, 0x59, 0x33, 0x6d, + 0x4b, 0x64, 0x6e, 0x64, 0x68, 0x51, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4c, + 0x49, 0x43, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00, +}; +const size_t PUBLIC_KEY_LEN = 451; diff --git a/libraries/ArduinoOTA/keywords.txt b/libraries/ArduinoOTA/keywords.txt index 9774de881ea..9c8a81df3ae 100644 --- a/libraries/ArduinoOTA/keywords.txt +++ b/libraries/ArduinoOTA/keywords.txt @@ -29,6 +29,7 @@ setPartitionLabel KEYWORD2 getPartitionLabel KEYWORD2 setRebootOnSuccess KEYWORD2 setMdnsEnabled KEYWORD2 +setSignature KEYWORD2 getCommand KEYWORD2 setTimeout KEYWORD2 diff --git a/libraries/ArduinoOTA/library.properties b/libraries/ArduinoOTA/library.properties index c4e5ff88a65..604e21d2819 100644 --- a/libraries/ArduinoOTA/library.properties +++ b/libraries/ArduinoOTA/library.properties @@ -1,5 +1,5 @@ name=ArduinoOTA -version=3.3.2 +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 a5b0d09de58..d20cfb07aa9 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/src/ArduinoOTA.cpp @@ -26,9 +26,15 @@ // #define OTA_DEBUG Serial -ArduinoOTAClass::ArduinoOTAClass() - : _port(0), _initialized(false), _rebootOnSuccess(true), _mdnsEnabled(true), _state(OTA_IDLE), _size(0), _cmd(0), _ota_port(0), _ota_timeout(1000), - _start_callback(NULL), _end_callback(NULL), _error_callback(NULL), _progress_callback(NULL) {} +ArduinoOTAClass::ArduinoOTAClass(UpdateClass *updater) + : _updater(updater), _port(0), _initialized(false), _rebootOnSuccess(true), _mdnsEnabled(true), _state(OTA_IDLE), _size(0), _cmd(0), _ota_port(0), + _ota_timeout(1000), _start_callback(NULL), _end_callback(NULL), _error_callback(NULL), _progress_callback(NULL) +#ifdef UPDATE_SIGN + , + _sign(NULL) +#endif /* UPDATE_SIGN */ +{ +} ArduinoOTAClass::~ArduinoOTAClass() { end(); @@ -136,6 +142,21 @@ ArduinoOTAClass &ArduinoOTAClass::setMdnsEnabled(bool enabled) { return *this; } +#ifdef UPDATE_SIGN +ArduinoOTAClass &ArduinoOTAClass::setSignature(UpdaterVerifyClass *sign) { + if (_state == OTA_IDLE && sign) { + _sign = sign; + int hashType = sign->getHashType(); + [[maybe_unused]] + const char *hashName = (hashType == HASH_SHA256) ? "SHA-256" + : (hashType == HASH_SHA384) ? "SHA-384" + : "SHA-512"; + log_i("Signature verification enabled for ArduinoOTA (hash: %s)", hashName); + } + return *this; +} +#endif /* UPDATE_SIGN */ + void ArduinoOTAClass::begin() { if (_initialized) { log_w("already initialized"); @@ -297,10 +318,30 @@ void ArduinoOTAClass::_onRx() { } void ArduinoOTAClass::_runUpdate() { + if (!_updater) { + log_e("UpdateClass is NULL!"); + return; + } + +#ifdef UPDATE_SIGN + // Install signature verification if enabled + if (_sign) { + if (!_updater->installSignature(_sign)) { + log_e("Failed to install signature verification"); + if (_error_callback) { + _error_callback(OTA_BEGIN_ERROR); + } + _state = OTA_IDLE; + return; + } + log_i("Signature verification installed for OTA update"); + } +#endif /* UPDATE_SIGN */ + const char *partition_label = _partition_label.length() ? _partition_label.c_str() : NULL; - if (!Update.begin(_size, _cmd, -1, LOW, partition_label)) { + if (!_updater->begin(_size, _cmd, -1, LOW, partition_label)) { - log_e("Begin ERROR: %s", Update.errorString()); + log_e("Begin ERROR: %s", _updater->errorString()); if (_error_callback) { _error_callback(OTA_BEGIN_ERROR); @@ -309,7 +350,7 @@ void ArduinoOTAClass::_runUpdate() { return; } - Update.setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication + _updater->setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication if (_start_callback) { _start_callback(); @@ -328,7 +369,7 @@ void ArduinoOTAClass::_runUpdate() { uint32_t written = 0, total = 0, tried = 0; - while (!Update.isFinished() && client.connected()) { + while (!_updater->isFinished() && client.connected()) { size_t waited = _ota_timeout; size_t available = client.available(); while (!available && waited) { @@ -351,7 +392,7 @@ void ArduinoOTAClass::_runUpdate() { _error_callback(OTA_RECEIVE_ERROR); } _state = OTA_IDLE; - Update.abort(); + _updater->abort(); return; } if (!available) { @@ -373,7 +414,7 @@ void ArduinoOTAClass::_runUpdate() { } } - written = Update.write(buf, r); + written = _updater->write(buf, r); if (written > 0) { if (written != r) { log_w("didn't write enough! %u != %u", written, r); @@ -386,11 +427,11 @@ void ArduinoOTAClass::_runUpdate() { _progress_callback(total, _size); } } else { - log_e("Write ERROR: %s", Update.errorString()); + log_e("Write ERROR: %s", _updater->errorString()); } } - if (Update.end()) { + if (_updater->end()) { client.print("OK"); client.stop(); delay(10); @@ -406,10 +447,10 @@ void ArduinoOTAClass::_runUpdate() { if (_error_callback) { _error_callback(OTA_END_ERROR); } - Update.printError(client); + _updater->printError(client); client.stop(); delay(10); - log_e("Update ERROR: %s", Update.errorString()); + log_e("Update ERROR: %s", _updater->errorString()); _state = OTA_IDLE; } } @@ -448,6 +489,11 @@ void ArduinoOTAClass::setTimeout(int timeoutInMillis) { _ota_timeout = timeoutInMillis; } +ArduinoOTAClass &ArduinoOTAClass::setUpdaterInstance(UpdateClass *updater) { + _updater = updater; + return *this; +} + #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ARDUINOOTA) ArduinoOTAClass ArduinoOTA; #endif diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.h b/libraries/ArduinoOTA/src/ArduinoOTA.h index a946388c4aa..e291f1b9abe 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.h +++ b/libraries/ArduinoOTA/src/ArduinoOTA.h @@ -41,7 +41,11 @@ class ArduinoOTAClass { typedef std::function THandlerFunction_Error; typedef std::function THandlerFunction_Progress; - ArduinoOTAClass(); +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE) + ArduinoOTAClass(UpdateClass *updater = &Update); +#else + ArduinoOTAClass(UpdateClass *updater = nullptr); +#endif ~ArduinoOTAClass(); //Sets the service port. Default 3232 @@ -61,12 +65,23 @@ class ArduinoOTAClass { ArduinoOTAClass &setPartitionLabel(const char *partition_label); String getPartitionLabel(); + //Sets instance of UpdateClass to perform updating operations + ArduinoOTAClass &setUpdaterInstance(UpdateClass *updater); + //Sets if the device should be rebooted after successful update. Default true ArduinoOTAClass &setRebootOnSuccess(bool reboot); //Sets if the device should advertise itself to Arduino IDE. Default true ArduinoOTAClass &setMdnsEnabled(bool enabled); +#ifdef UPDATE_SIGN + //Install signature verification for OTA updates + //Must be called before begin() + //sign: Signature verifier to use (e.g., UpdaterRSAVerifier or UpdaterECDSAVerifier) + // The hash type is determined from the verifier's configuration + ArduinoOTAClass &setSignature(UpdaterVerifyClass *sign); +#endif /* UPDATE_SIGN */ + //This callback will be called when OTA connection has begun ArduinoOTAClass &onStart(THandlerFunction fn); @@ -94,6 +109,7 @@ class ArduinoOTAClass { void setTimeout(int timeoutInMillis); private: + UpdateClass *_updater; int _port; String _password; String _hostname; @@ -116,6 +132,10 @@ class ArduinoOTAClass { THandlerFunction_Error _error_callback; THandlerFunction_Progress _progress_callback; +#ifdef UPDATE_SIGN + UpdaterVerifyClass *_sign; +#endif /* UPDATE_SIGN */ + void _runUpdate(void); void _onRx(void); int parseInt(void); 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 24b48d2e4de..0dc51eae1f7 100644 --- a/libraries/AsyncUDP/library.properties +++ b/libraries/AsyncUDP/library.properties @@ -1,5 +1,5 @@ name=ESP32 Async UDP -version=3.3.2 +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 decd35106ec..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; 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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Beacon_Scanner/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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/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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Client/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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_Server/Client_Server.ino b/libraries/BLE/examples/Client_Server/Client_Server.ino new file mode 100644 index 00000000000..af8db874bc4 --- /dev/null +++ b/libraries/BLE/examples/Client_Server/Client_Server.ino @@ -0,0 +1,280 @@ +/* + * BLE Client and Server Coexistence Example + * + * This example demonstrates how to run both BLE client and server + * functionality on the same ESP32 device simultaneously. + * + * The device will: + * - Act as a BLE server, advertising a service with a characteristic + * - Act as a BLE client, scanning for other BLE servers + * - Connect to found servers and interact with their services + * - Handle both incoming and outgoing connections + * + * You can test this example by uploading it to two ESP32 boards. + * + * Author: lucasssvaz + * Based on Arduino BLE examples + */ + +#include +#include +#include +#include +#include + +// Server-side definitions +#define SERVER_SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define SERVER_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +// Client-side definitions (looking for the same service) +static BLEUUID clientServiceUUID(SERVER_SERVICE_UUID); +static BLEUUID clientCharUUID(SERVER_CHARACTERISTIC_UUID); + +// Server objects +BLEServer *pServer = nullptr; +BLECharacteristic *pServerCharacteristic = nullptr; + +// Client objects +static boolean doConnect = false; +static boolean clientConnected = false; +static BLERemoteCharacteristic *pRemoteCharacteristic; +static BLEAdvertisedDevice *targetDevice; +static BLEClient *pClient = nullptr; +BLEScan *pBLEScan = nullptr; + +// Server callbacks +class ServerCallbacks : public BLEServerCallbacks { + void onConnect(BLEServer *pServer) { + Serial.println("Server: Client connected"); + } + + void onDisconnect(BLEServer *pServer) { + Serial.println("Server: Client disconnected"); + // Restart advertising + BLEDevice::startAdvertising(); + } +}; + +// Characteristic callbacks for server +class CharacteristicCallbacks : public BLECharacteristicCallbacks { + void onWrite(BLECharacteristic *pCharacteristic) { + String value = pCharacteristic->getValue(); + Serial.print("Server: Characteristic written, value: "); + Serial.println(value.c_str()); + } + + void onRead(BLECharacteristic *pCharacteristic) { + Serial.println("Server: Characteristic read"); + } +}; + +// Client callbacks +class ClientCallbacks : public BLEClientCallbacks { + void onConnect(BLEClient *pClient) { + Serial.println("Client: Connected to server"); + clientConnected = true; + } + + void onDisconnect(BLEClient *pClient) { + Serial.println("Client: Disconnected from server"); + clientConnected = false; + } +}; + +// Client notification callback +static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { + Serial.print("Client: Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("Client: Data: "); + Serial.write(pData, length); + Serial.println(); +} + +// Scan callbacks +class AdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("Client: Found device: "); + Serial.println(advertisedDevice.toString().c_str()); + + // Check if this device has our target service + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(clientServiceUUID) && !clientConnected) { + + Serial.println("Client: Found target service, attempting connection..."); + BLEDevice::getScan()->stop(); + targetDevice = new BLEAdvertisedDevice(advertisedDevice); + doConnect = true; + } + } +}; + +bool connectToServer() { + Serial.print("Client: Forming connection to "); + Serial.println(targetDevice->getAddress().toString().c_str()); + + // Create client if it doesn't exist, otherwise reuse existing one + if (pClient == nullptr) { + pClient = BLEDevice::createClient(); + pClient->setClientCallbacks(new ClientCallbacks()); + Serial.println("Client: Created new client"); + } else { + Serial.println("Client: Reusing existing client"); + } + + if (!pClient->connect(targetDevice)) { + Serial.println("Client: Failed to connect"); + return false; + } + + Serial.println("Client: Connected to server"); + pClient->setMTU(517); // Request maximum MTU + + // Get the service + BLERemoteService *pRemoteService = pClient->getService(clientServiceUUID); + if (pRemoteService == nullptr) { + Serial.print("Client: Failed to find service UUID: "); + Serial.println(clientServiceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println("Client: Found service"); + + // Get the characteristic + pRemoteCharacteristic = pRemoteService->getCharacteristic(clientCharUUID); + if (pRemoteCharacteristic == nullptr) { + Serial.print("Client: Failed to find characteristic UUID: "); + Serial.println(clientCharUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println("Client: Found characteristic"); + + // Read the initial value + if (pRemoteCharacteristic->canRead()) { + String value = pRemoteCharacteristic->readValue(); + Serial.print("Client: Initial characteristic value: "); + Serial.println(value.c_str()); + } + + // Register for notifications if available + if (pRemoteCharacteristic->canNotify()) { + pRemoteCharacteristic->registerForNotify(notifyCallback); + Serial.println("Client: Registered for notifications"); + } + + return true; +} + +void setupServer() { + Serial.println("Setting up BLE Server..."); + + // Create server + pServer = BLEDevice::createServer(); + pServer->setCallbacks(new ServerCallbacks()); + + // Create service + BLEService *pService = pServer->createService(SERVER_SERVICE_UUID); + + // Create characteristic + pServerCharacteristic = pService->createCharacteristic( + SERVER_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY + ); + + pServerCharacteristic->setCallbacks(new CharacteristicCallbacks()); + pServerCharacteristic->setValue("Hello from Coexistence Server"); + + // Start service + pService->start(); + + // Start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVER_SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); + pAdvertising->setMinPreferred(0x12); + + BLEDevice::startAdvertising(); + + Serial.println("Server: Advertising started"); +} + +void setupClient() { + Serial.println("Setting up BLE Client..."); + + // Create scanner + pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks()); + pBLEScan->setActiveScan(true); + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); + + Serial.println("Client: Scanner configured"); +} + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE Client-Server Coexistence Example..."); + + // Initialize BLE device with a name + BLEDevice::init("ESP32-Coexistence"); + + // Setup both server and client + setupServer(); + setupClient(); + + // Start initial scan + pBLEScan->start(10, false); // Scan for 10 seconds, don't repeat + + Serial.println("Setup complete. Device is advertising as server and scanning as client."); +} + +void loop() { + static unsigned long lastServerUpdate = 0; + static unsigned long lastClientWrite = 0; + static unsigned long lastScanStart = 0; + unsigned long currentTime = millis(); + + // Handle client connection attempts + if (doConnect && !clientConnected) { + if (connectToServer()) { + Serial.println("Client: Successfully connected to remote server"); + } else { + Serial.println("Client: Failed to connect, will retry scanning"); + // Restart scanning after failed connection + pBLEScan->start(10, false); + } + doConnect = false; + } + + // Update server characteristic periodically + if (currentTime - lastServerUpdate > 5000) { // Every 5 seconds + String value = "Server time: " + String(millis() / 1000); + pServerCharacteristic->setValue(value.c_str()); + pServerCharacteristic->notify(); // Notify connected clients + Serial.print("Server: Updated characteristic to: "); + Serial.println(value); + + lastServerUpdate = currentTime; + } + + // Write to remote characteristic if connected as client + if (clientConnected && pRemoteCharacteristic && currentTime - lastClientWrite > 3000) { + if (pRemoteCharacteristic->canWrite()) { + String clientValue = "Client msg: " + String(millis() / 1000); + pRemoteCharacteristic->writeValue(clientValue.c_str(), clientValue.length()); + Serial.print("Client: Wrote to remote characteristic: "); + Serial.println(clientValue); + lastClientWrite = currentTime; + } + } + + // Restart scanning periodically if not connected + if (!clientConnected && currentTime - lastScanStart > 15000) { // Every 15 seconds + Serial.println("Client: Restarting scan..."); + pBLEScan->start(10, false); + lastScanStart = currentTime; + } + + delay(100); +} diff --git a/libraries/BLE/examples/Client_Server/ci.yml b/libraries/BLE/examples/Client_Server/ci.yml new file mode 100644 index 00000000000..cfee8c8935f --- /dev/null +++ b/libraries/BLE/examples/Client_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/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 index ec3c457221c..a3204fcb044 100644 --- 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 @@ -1,10 +1,16 @@ /* - Secure client with static passkey + 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. @@ -12,9 +18,10 @@ 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). + 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. @@ -36,10 +43,59 @@ static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8"); 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 "); @@ -62,11 +118,30 @@ class MyClientCallback : public BLEClientCallbacks { } }; +// 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()); - BLEClient *pClient = BLEDevice::createClient(); + pClient = BLEDevice::createClient(); Serial.println(" - Created client"); pClient->setClientCallbacks(new MyClientCallback()); @@ -192,8 +267,9 @@ void setup() { pSecurity->setPassKey(true, CLIENT_PIN); // Set authentication mode to match server requirements - // Enable secure connection and MITM (for password prompts) for this example - pSecurity->setAuthenticationMode(false, true, true); + // 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 @@ -201,6 +277,9 @@ void setup() { // 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. diff --git a/libraries/BLE/examples/Client_secure_static_passkey/ci.json b/libraries/BLE/examples/Client_secure_static_passkey/ci.json deleted file mode 100644 index e9657aad729..00000000000 --- a/libraries/BLE/examples/Client_secure_static_passkey/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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/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 e9657aad729..00000000000 --- a/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/EddystoneURL_Beacon/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Notify/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Scan/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Server/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Server_multiconnect/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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/ci.json b/libraries/BLE/examples/Server_secure_authorization/ci.json deleted file mode 100644 index 1e2d20da791..00000000000 --- a/libraries/BLE/examples/Server_secure_authorization/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "targets": { - "esp32": false - }, - "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" - ] -} 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 index 79b73695202..fef8c0bd15f 100644 --- 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 @@ -40,6 +40,73 @@ // 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!"); @@ -76,8 +143,11 @@ void setup() { pSecurity->setCapability(ESP_IO_CAP_OUT); // Set authentication mode - // Require secure connection and MITM (for password prompts) for this example - pSecurity->setAuthenticationMode(false, true, true); + // 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); diff --git a/libraries/BLE/examples/Server_secure_static_passkey/ci.json b/libraries/BLE/examples/Server_secure_static_passkey/ci.json deleted file mode 100644 index e9657aad729..00000000000 --- a/libraries/BLE/examples/Server_secure_static_passkey/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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_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 e9657aad729..00000000000 --- a/libraries/BLE/examples/UART/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/Write/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 e9657aad729..00000000000 --- a/libraries/BLE/examples/iBeacon/ci.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "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.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 730ab0ea315..f4faf62a79e 100644 --- a/libraries/BLE/library.properties +++ b/libraries/BLE/library.properties @@ -1,5 +1,5 @@ name=BLE -version=3.3.2 +version=3.3.4 author=Neil Kolban maintainer=lucasssvaz sentence=BLE functions for ESP32 diff --git a/libraries/BLE/src/BLEAdvertisedDevice.cpp b/libraries/BLE/src/BLEAdvertisedDevice.cpp index 8a9f7349d0f..40ff78c6a7d 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.cpp +++ b/libraries/BLE/src/BLEAdvertisedDevice.cpp @@ -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; diff --git a/libraries/BLE/src/BLEAdvertisedDevice.h b/libraries/BLE/src/BLEAdvertisedDevice.h index 3f1bc6f3c4f..2a34e5a43ca 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.h +++ b/libraries/BLE/src/BLEAdvertisedDevice.h @@ -74,6 +74,9 @@ class BLEAdvertisedDevice { ***************************************************************************/ BLEAdvertisedDevice(); + ~BLEAdvertisedDevice(); + BLEAdvertisedDevice(const BLEAdvertisedDevice &other); + BLEAdvertisedDevice &operator=(const BLEAdvertisedDevice &other); BLEAddress getAddress(); uint16_t getAppearance(); String getManufacturerData(); diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp index a492116b2e6..cb971796b31 100644 --- a/libraries/BLE/src/BLECharacteristic.cpp +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -904,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; @@ -955,7 +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, &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; } diff --git a/libraries/BLE/src/BLECharacteristic.h b/libraries/BLE/src/BLECharacteristic.h index b0ab8f916ea..f7f61cde313 100644 --- a/libraries/BLE/src/BLECharacteristic.h +++ b/libraries/BLE/src/BLECharacteristic.h @@ -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 @@ -246,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 /*************************************************************************** @@ -271,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 diff --git a/libraries/BLE/src/BLEDevice.cpp b/libraries/BLE/src/BLEDevice.cpp index d56786e8e18..c184fada279 100644 --- a/libraries/BLE/src/BLEDevice.cpp +++ b/libraries/BLE/src/BLEDevice.cpp @@ -38,6 +38,7 @@ #include "BLEUtils.h" #include "GeneralUtils.h" #include "BLESecurity.h" +#include "base64.h" #if defined(ARDUINO_ARCH_ESP32) #include "esp32-hal-bt.h" @@ -463,7 +464,7 @@ int BLEDevice::getPower(esp_ble_power_type_t 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; @@ -608,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(); @@ -691,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(); @@ -717,19 +927,20 @@ void BLEDevice::deinit(bool release_memory) { 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); #endif - } else { -#ifdef CONFIG_NIMBLE_ENABLED - m_synced = false; #endif - initialized = false; } + +#ifdef CONFIG_NIMBLE_ENABLED + m_synced = false; #endif + initialized = false; } void BLEDevice::setCustomGapHandler(gap_event_handler handler) { @@ -960,6 +1171,10 @@ void BLEDevice::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_par { 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); } @@ -1004,6 +1219,23 @@ void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { #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. diff --git a/libraries/BLE/src/BLEDevice.h b/libraries/BLE/src/BLEDevice.h index ba29c735afd..5d4a16295a8 100644 --- a/libraries/BLE/src/BLEDevice.h +++ b/libraries/BLE/src/BLEDevice.h @@ -192,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(); @@ -230,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: diff --git a/libraries/BLE/src/BLERemoteCharacteristic.cpp b/libraries/BLE/src/BLERemoteCharacteristic.cpp index d26e2970a10..70a91288ab2 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.cpp +++ b/libraries/BLE/src/BLERemoteCharacteristic.cpp @@ -112,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 @@ -132,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) { @@ -287,6 +297,7 @@ void BLERemoteCharacteristic::removeDescriptors() { delete myPair.second; } m_descriptorMap.clear(); + m_descriptorsRetrieved = false; // Allow descriptors to be retrieved again } // removeCharacteristics /** @@ -366,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"); @@ -549,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 @@ -565,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. @@ -607,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( @@ -655,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 @@ -773,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; } @@ -781,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)); @@ -798,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 @@ -958,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"); diff --git a/libraries/BLE/src/BLERemoteCharacteristic.h b/libraries/BLE/src/BLERemoteCharacteristic.h index 5191087d11c..7b04a15c7cb 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.h +++ b/libraries/BLE/src/BLERemoteCharacteristic.h @@ -152,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 * @@ -159,7 +160,6 @@ class BLERemoteCharacteristic { #if defined(CONFIG_NIMBLE_ENABLED) uint16_t m_defHandle; - uint16_t m_endHandle; #endif /*************************************************************************** diff --git a/libraries/BLE/src/BLESecurity.cpp b/libraries/BLE/src/BLESecurity.cpp index 7e9269e6000..76757f52135 100644 --- a/libraries/BLE/src/BLESecurity.cpp +++ b/libraries/BLE/src/BLESecurity.cpp @@ -46,6 +46,7 @@ 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; @@ -59,6 +60,7 @@ uint32_t BLESecurity::m_passkey = BLE_SM_DEFAULT_PASSKEY; #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 /*************************************************************************** @@ -191,6 +193,7 @@ void BLESecurity::regenPassKeyOnConnect(bool enable) { 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. @@ -263,6 +266,48 @@ bool BLESecurityCallbacks::onAuthorizationRequest(uint16_t connHandle, uint16_t 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 +} + /*************************************************************************** * Bluedroid functions * ***************************************************************************/ @@ -285,6 +330,18 @@ bool BLESecurity::startSecurity(esp_bd_addr_t bd_addr, int *rcPtr) { } 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)); diff --git a/libraries/BLE/src/BLESecurity.h b/libraries/BLE/src/BLESecurity.h index 2d2306231ac..fc6ff38f6ca 100644 --- a/libraries/BLE/src/BLESecurity.h +++ b/libraries/BLE/src/BLESecurity.h @@ -25,6 +25,7 @@ #include "BLEDevice.h" #include "BLEClient.h" #include "BLEServer.h" +#include "RTOS.h" /*************************************************************************** * Bluedroid includes * @@ -104,6 +105,8 @@ class BLESecurity { 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 * @@ -140,6 +143,7 @@ class BLESecurity { 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; @@ -153,6 +157,7 @@ class BLESecurity { #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 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 3ced43c52ea..03d5687a49e 100644 --- a/libraries/BluetoothSerial/library.properties +++ b/libraries/BluetoothSerial/library.properties @@ -1,5 +1,5 @@ name=BluetoothSerial -version=3.3.2 +version=3.3.4 author=Evandro Copercini maintainer=Evandro Copercini sentence=Simple UART to Classical Bluetooth bridge for ESP32 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 e30f5a39555..6d124cace69 100644 --- a/libraries/DNSServer/library.properties +++ b/libraries/DNSServer/library.properties @@ -1,5 +1,5 @@ name=DNSServer -version=3.3.2 +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 e6728c5d808..bf03f1a66e1 100644 --- a/libraries/EEPROM/library.properties +++ b/libraries/EEPROM/library.properties @@ -1,5 +1,5 @@ name=EEPROM -version=3.3.2 +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/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/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/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/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/ci.json b/libraries/ESP32/examples/Touch/TouchButton/ci.json deleted file mode 100644 index c0ecf9fc0a5..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButton/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=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/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/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 864c315224b..ef14a246302 100644 --- a/libraries/ESP32/library.properties +++ b/libraries/ESP32/library.properties @@ -1,5 +1,5 @@ name=ESP32 -version=3.3.2 +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 7145fb7dccd..da985780ca7 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -1,5 +1,5 @@ name=ESP_I2S -version=3.3.2 +version=3.3.4 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication diff --git a/libraries/ESP_I2S/src/ESP_I2S.cpp b/libraries/ESP_I2S/src/ESP_I2S.cpp index 6bf8089e48a..e2c6d670b9b 100644 --- a/libraries/ESP_I2S/src/ESP_I2S.cpp +++ b/libraries/ESP_I2S/src/ESP_I2S.cpp @@ -192,6 +192,7 @@ static esp_err_t i2s_channel_read_16_stereo_to_mono(i2s_chan_handle_t handle, ch I2SClass::I2SClass() { last_error = ESP_OK; + _mode = I2S_MODE_MAX; // Initialize to invalid mode to indicate I2S not started tx_chan = NULL; tx_sample_rate = 0; @@ -239,7 +240,9 @@ I2SClass::~I2SClass() { bool I2SClass::i2sDetachBus(void *bus_pointer) { I2SClass *bus = (I2SClass *)bus_pointer; - if (bus->tx_chan != NULL || bus->tx_chan != NULL) { + // Only call end() if I2S has been initialized (begin() was called) + // _mode is set to I2S_MODE_MAX in constructor and to a valid mode in begin() + if (bus->_mode < I2S_MODE_MAX) { bus->end(); } return true; @@ -703,6 +706,16 @@ bool I2SClass::begin(i2s_mode_t mode, uint32_t rate, i2s_data_bit_width_t bits_c } bool I2SClass::end() { + // Check if already ended to prevent recursion + if (_mode >= I2S_MODE_MAX) { + return true; + } + + // Save mode and reset it BEFORE clearing pins to prevent recursive calls + // When perimanClearPinBus() is called, it may trigger i2sDetachBus() again + i2s_mode_t mode = _mode; + _mode = I2S_MODE_MAX; + if (tx_chan != NULL) { I2S_ERROR_CHECK_RETURN_FALSE(i2s_channel_disable(tx_chan)); I2S_ERROR_CHECK_RETURN_FALSE(i2s_del_channel(tx_chan)); @@ -720,7 +733,7 @@ bool I2SClass::end() { } //Peripheral manager deinit used pins - switch (_mode) { + switch (mode) { case I2S_MODE_STD: #if SOC_I2S_SUPPORTS_TDM case I2S_MODE_TDM: 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/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 a1868930307..cae0ed03dc3 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -1,5 +1,5 @@ name=ESP_NOW -version=3.3.2 +version=3.3.4 author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW 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 6bf612a1263..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); diff --git a/libraries/ESP_SR/examples/Basic/ci.json b/libraries/ESP_SR/examples/Basic/ci.json deleted file mode 100644 index ed7699a7857..00000000000 --- a/libraries/ESP_SR/examples/Basic/ci.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "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/examples/Basic/ci.yml b/libraries/ESP_SR/examples/Basic/ci.yml new file mode 100644 index 00000000000..31563e1d016 --- /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,ChipVariant=postv3,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 b39120f015d..5e019e501a2 100644 --- a/libraries/ESP_SR/library.properties +++ b/libraries/ESP_SR/library.properties @@ -1,5 +1,5 @@ name=ESP_SR -version=3.3.2 +version=3.3.4 author=me-no-dev maintainer=me-no-dev sentence=Library for ESP Sound Recognition 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 4d0f1d4eef9..44a14541f40 100644 --- a/libraries/ESPmDNS/library.properties +++ b/libraries/ESPmDNS/library.properties @@ -1,5 +1,5 @@ name=ESPmDNS -version=3.3.2 +version=3.3.4 author=Hristo Gochkov, Ivan Grokhtkov maintainer=Hristo Gochkov sentence=ESP32 mDNS Library diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h index 74f9002461f..1fb90bbf454 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.h +++ b/libraries/ESPmDNS/src/ESPmDNS.h @@ -127,9 +127,7 @@ class MDNSResponder { mdns_txt_item_t *_getResultTxt(int idx, int txtIdx); }; -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MDNS) extern MDNSResponder MDNS; -#endif #endif /* CONFIG_MDNS_MAX_INTERFACES */ #endif //ESP32MDNS_H 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 a9bff23f7f1..bc342dff285 100644 --- a/libraries/Ethernet/library.properties +++ b/libraries/Ethernet/library.properties @@ -1,5 +1,5 @@ name=Ethernet -version=3.3.2 +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 7d808c620d8..8d1e459030c 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -53,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) { @@ -107,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 @@ -146,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; @@ -302,6 +308,7 @@ bool ETHClass::begin(eth_phy_type_t type, int32_t phy_addr, int mdc, int mdio, i } if (_phy == NULL) { log_e("esp_eth_phy_new failed"); + _delMacAndPhy(); return false; } @@ -735,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; } @@ -753,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 @@ -920,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); @@ -951,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) { @@ -1171,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 f609c651102..ceeaf789848 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -271,6 +271,8 @@ class ETHClass : public NetworkInterface { bool _setLinkSpeed(uint16_t speed); bool _setAutoNegotiation(bool on); + void _delMacAndPhy(); + friend class EthernetClass; // to access beginSPI }; 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 a94339dc87e..707c593d9d2 100644 --- a/libraries/FFat/library.properties +++ b/libraries/FFat/library.properties @@ -1,5 +1,5 @@ name=FFat -version=3.3.2 +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/FS/library.properties b/libraries/FS/library.properties index 8dac5fc3235..1ff59d3308d 100644 --- a/libraries/FS/library.properties +++ b/libraries/FS/library.properties @@ -1,5 +1,5 @@ name=FS -version=3.3.2 +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 da22ea1962c..9afb4999119 100644 --- a/libraries/FS/src/vfs_api.cpp +++ b/libraries/FS/src/vfs_api.cpp @@ -276,8 +276,12 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs if (!_f) { log_e("fopen(%s) failed", temp); } - if (_f && (_stat.st_blksize == 0)) { - setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); + 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/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/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/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/ci.json b/libraries/HTTPClient/examples/CustomHeaders/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/HTTPClient/examples/CustomHeaders/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/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/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/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 79717c13b37..ffbcc9101be 100644 --- a/libraries/HTTPClient/library.properties +++ b/libraries/HTTPClient/library.properties @@ -1,5 +1,5 @@ name=HTTPClient -version=3.3.2 +version=3.3.4 author=Markus Sattler maintainer=Markus Sattler sentence=HTTP Client for ESP32 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/library.properties b/libraries/HTTPUpdate/library.properties index c61ceee9fac..dcf5f1219d7 100644 --- a/libraries/HTTPUpdate/library.properties +++ b/libraries/HTTPUpdate/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdate -version=3.3.2 +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 5183afac017..3a54572a925 100644 --- a/libraries/HTTPUpdate/src/HTTPUpdate.cpp +++ b/libraries/HTTPUpdate/src/HTTPUpdate.cpp @@ -35,15 +35,8 @@ // To do extern "C" uint32_t _SPIFFS_start; // To do extern "C" uint32_t _SPIFFS_end; -HTTPUpdate::HTTPUpdate(void) : HTTPUpdate(8000) {} - -HTTPUpdate::HTTPUpdate(int httpClientTimeout) : _httpClientTimeout(httpClientTimeout), _ledPin(-1) { - _followRedirects = HTTPC_DISABLE_FOLLOW_REDIRECTS; - _md5Sum = String(); - _user = String(); - _password = String(); - _auth = String(); -} +HTTPUpdate::HTTPUpdate(int httpClientTimeout, UpdateClass *updater) + : _httpClientTimeout(httpClientTimeout), _updater(updater), _followRedirects(HTTPC_DISABLE_FOLLOW_REDIRECTS) {} HTTPUpdate::~HTTPUpdate(void) {} @@ -129,6 +122,9 @@ int HTTPUpdate::getLastError(void) { * @return String error */ String HTTPUpdate::getLastErrorString(void) { + if (!_updater) { + return {}; + } if (_lastError == 0) { return String(); // no error @@ -137,7 +133,7 @@ String HTTPUpdate::getLastErrorString(void) { // error from Update class if (_lastError > 0) { StreamString error; - Update.printError(error); + _updater->printError(error); error.trim(); // remove line ending return String("Update error: ") + error; } @@ -444,16 +440,19 @@ HTTPUpdateResult HTTPUpdate::handleUpdate(HTTPClient &http, const String ¤ * @return true if Update ok */ bool HTTPUpdate::runUpdate(Stream &in, uint32_t size, String md5, int command) { + if (!_updater) { + return false; + } StreamString error; if (_cbProgress) { - Update.onProgress(_cbProgress); + _updater->onProgress(_cbProgress); } - if (!Update.begin(size, command, _ledPin, _ledOn)) { - _lastError = Update.getError(); - Update.printError(error); + if (!_updater->begin(size, command, _ledPin, _ledOn)) { + _lastError = _updater->getError(); + _updater->printError(error); error.trim(); // remove line ending log_e("Update.begin failed! (%s)\n", error.c_str()); return false; @@ -464,7 +463,7 @@ bool HTTPUpdate::runUpdate(Stream &in, uint32_t size, String md5, int command) { } if (md5.length()) { - if (!Update.setMD5(md5.c_str())) { + if (!_updater->setMD5(md5.c_str())) { _lastError = HTTP_UE_SERVER_FAULTY_MD5; log_e("Update.setMD5 failed! (%s)\n", md5.c_str()); return false; @@ -473,9 +472,9 @@ bool HTTPUpdate::runUpdate(Stream &in, uint32_t size, String md5, int command) { // To do: the SHA256 could be checked if the server sends it - if (Update.writeStream(in) != size) { - _lastError = Update.getError(); - Update.printError(error); + if (_updater->writeStream(in) != size) { + _lastError = _updater->getError(); + _updater->printError(error); error.trim(); // remove line ending log_e("Update.writeStream failed! (%s)\n", error.c_str()); return false; @@ -485,9 +484,9 @@ bool HTTPUpdate::runUpdate(Stream &in, uint32_t size, String md5, int command) { _cbProgress(size, size); } - if (!Update.end()) { - _lastError = Update.getError(); - Update.printError(error); + if (!_updater->end()) { + _lastError = _updater->getError(); + _updater->printError(error); error.trim(); // remove line ending log_e("Update.end failed! (%s)\n", error.c_str()); return false; diff --git a/libraries/HTTPUpdate/src/HTTPUpdate.h b/libraries/HTTPUpdate/src/HTTPUpdate.h index ad38701b948..9517c0b45c4 100644 --- a/libraries/HTTPUpdate/src/HTTPUpdate.h +++ b/libraries/HTTPUpdate/src/HTTPUpdate.h @@ -58,8 +58,13 @@ using HTTPUpdateProgressCB = std::function; class HTTPUpdate { public: - HTTPUpdate(void); - HTTPUpdate(int httpClientTimeout); +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE) + HTTPUpdate(UpdateClass *updater = &Update) : HTTPUpdate(8000, updater){}; + HTTPUpdate(int httpClientTimeout, UpdateClass *updater = &Update); +#else + HTTPUpdate(UpdateClass *updater = nullptr) : HTTPUpdate(8000, updater){}; + HTTPUpdate(int httpClientTimeout, UpdateClass *updater = nullptr); +#endif ~HTTPUpdate(void); void rebootOnUpdate(bool reboot) { @@ -92,6 +97,11 @@ class HTTPUpdate { _auth = auth; } + //Sets instance of UpdateClass to perform updating operations + void setUpdaterInstance(UpdateClass *updater) { + _updater = updater; + }; + t_httpUpdate_return update(NetworkClient &client, const String &url, const String ¤tVersion = "", HTTPUpdateRequestCB requestCB = NULL); t_httpUpdate_return update( @@ -143,6 +153,7 @@ class HTTPUpdate { private: int _httpClientTimeout; + UpdateClass *_updater; followRedirects_t _followRedirects; String _user; String _password; @@ -155,7 +166,7 @@ class HTTPUpdate { HTTPUpdateErrorCB _cbError; HTTPUpdateProgressCB _cbProgress; - int _ledPin; + int _ledPin{-1}; uint8_t _ledOn; }; 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 e391d05ae91..bd034861f1e 100644 --- a/libraries/HTTPUpdateServer/library.properties +++ b/libraries/HTTPUpdateServer/library.properties @@ -1,5 +1,5 @@ name=HTTPUpdateServer -version=3.3.2 +version=3.3.4 author=Hristo Kapanakov maintainer= sentence=Simple HTTP Update server based on the WebServer diff --git a/libraries/Hash/library.properties b/libraries/Hash/library.properties index 6e7ed56919c..c497c42f71e 100644 --- a/libraries/Hash/library.properties +++ b/libraries/Hash/library.properties @@ -1,5 +1,5 @@ name=Hash -version=3.3.2 +version=3.3.4 author=lucasssvaz maintainer=lucasssvaz sentence=Bundle of hashing functions for the ESP32 diff --git a/libraries/Hash/src/PBKDF2_HMACBuilder.cpp b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp index 125d4bcb061..ec06bc642e5 100644 --- a/libraries/Hash/src/PBKDF2_HMACBuilder.cpp +++ b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp @@ -111,6 +111,8 @@ void PBKDF2_HMACBuilder::add(const uint8_t *data, size_t 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; } 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 24904296fe8..27285e999fe 100644 --- a/libraries/Insights/library.properties +++ b/libraries/Insights/library.properties @@ -1,5 +1,5 @@ name=ESP Insights -version=3.3.2 +version=3.3.4 author=Sanket Wadekar maintainer=Sanket Wadekar sentence=ESP Insights 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 e94daaa9602..f5ad750a315 100644 --- a/libraries/LittleFS/library.properties +++ b/libraries/LittleFS/library.properties @@ -1,5 +1,5 @@ name=LittleFS -version=3.3.2 +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/Matter/examples/MatterColorLight/README.md b/libraries/Matter/examples/MatterColorLight/README.md new file mode 100644 index 00000000000..7781d23d703 --- /dev/null +++ b/libraries/Matter/examples/MatterColorLight/README.md @@ -0,0 +1,174 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..b02f051dbcf --- /dev/null +++ b/libraries/Matter/examples/MatterCommissionTest/README.md @@ -0,0 +1,159 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..841f392fe0b --- /dev/null +++ b/libraries/Matter/examples/MatterComposedLights/README.md @@ -0,0 +1,187 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..e842412c7e2 --- /dev/null +++ b/libraries/Matter/examples/MatterContactSensor/README.md @@ -0,0 +1,182 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..d95d645f13f --- /dev/null +++ b/libraries/Matter/examples/MatterDimmableLight/README.md @@ -0,0 +1,180 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..c92df9b3844 --- /dev/null +++ b/libraries/Matter/examples/MatterDimmablePlugin/README.md @@ -0,0 +1,226 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..1b229bf4291 --- /dev/null +++ b/libraries/Matter/examples/MatterEnhancedColorLight/README.md @@ -0,0 +1,187 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..45e712eeb8d --- /dev/null +++ b/libraries/Matter/examples/MatterEvents/README.md @@ -0,0 +1,189 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..19d5bae2555 --- /dev/null +++ b/libraries/Matter/examples/MatterFan/README.md @@ -0,0 +1,204 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..9058a477691 --- /dev/null +++ b/libraries/Matter/examples/MatterHumiditySensor/README.md @@ -0,0 +1,201 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..16e0c2d403f --- /dev/null +++ b/libraries/Matter/examples/MatterLambdaSingleCallbackManyEPs/README.md @@ -0,0 +1,214 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..53c9e085730 --- /dev/null +++ b/libraries/Matter/examples/MatterMinimum/README.md @@ -0,0 +1,180 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..95148d76e66 --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancySensor/README.md @@ -0,0 +1,277 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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/MatterOccupancyWithHoldTime/MatterOccupancyWithHoldTime.ino b/libraries/Matter/examples/MatterOccupancyWithHoldTime/MatterOccupancyWithHoldTime.ino new file mode 100644 index 00000000000..0eb6fe4113e --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancyWithHoldTime/MatterOccupancyWithHoldTime.ino @@ -0,0 +1,230 @@ +// 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 Occupancy Sensor Device. + * The Occupancy Sensor will be simulated to toggle occupancy every 2 minutes. + * When occupancy is detected, the sensor holds the "occupied" state for the HoldTime duration + * (default: 30 seconds, configurable via Matter Controller). After HoldTime expires, + * the sensor automatically switches to "unoccupied" state. + * + * The HoldTime attribute allows you to adjust how long the active status is retained + * after the person leaves. The HoldTime can be changed by a Matter Controller, and the + * onHoldTimeChange() callback is used to update the simulated sensor functionality. + * + * The HoldTime value is persisted to Preferences (NVS) and restored on reboot, so the + * last configured HoldTime value is maintained across device restarts. + * + * 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 +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +// 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 +#include + +// HoldTime configuration constants +const uint16_t HOLD_TIME_MIN = 0; // Minimum HoldTime in seconds +const uint16_t HOLD_TIME_MAX = 3600; // Maximum HoldTime in seconds (1 hour) +const uint16_t HOLD_TIME_DEFAULT = 30; // Default HoldTime in seconds + +// List of Matter Endpoints for this Node +// Matter Occupancy Sensor Endpoint +MatterOccupancySensor OccupancySensor; + +// Preferences to store HoldTime value across reboots +Preferences matterPref; +const char *holdTimePrefKey = "HoldTime"; + +// set your board USER BUTTON pin here - decommissioning only +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 decommissioningTimeout = 5000; // keep the button pressed for 5s, or longer, to decommission + +// Simulated Occupancy Sensor with HoldTime support +// When occupancy is detected, it holds the "occupied" state for HoldTime seconds +// After HoldTime expires, it automatically switches to "unoccupied" +// +// Behavior for different HoldTime vs detectionInterval relationships: +// - holdTime_ms < detectionInterval: State switches to unoccupied after HoldTime, then waits for next detection +// - holdTime_ms == detectionInterval: If detections keep coming, timer resets (continuous occupancy) +// - holdTime_ms > detectionInterval: If detections keep coming, timer resets (continuous occupancy) +// If detections stop, HoldTime expires after the last detection +bool simulatedHWOccupancySensor() { + static bool occupancyState = false; + static uint32_t lastDetectionTime = 0; + static uint32_t lastDetectionEvent = millis(); + const uint32_t detectionInterval = 120000; // Simulate detection every 2 minutes + + // Get current HoldTime from the sensor (can be changed by Matter Controller) + uint32_t holdTime_ms = OccupancySensor.getHoldTime() * 1000; // Convert seconds to milliseconds + + // Check HoldTime expiration FIRST (before processing new detections) + // This ensures HoldTime can expire even if a detection occurs in the same iteration + if (occupancyState && (millis() - lastDetectionTime > holdTime_ms)) { + occupancyState = false; + // Reset detection interval counter so next detection can happen immediately + // This makes the simulation more responsive after the room becomes unoccupied + lastDetectionEvent = millis(); + Serial.println("HoldTime expired. Switching to unoccupied state."); + } + + // Simulate periodic occupancy detection (e.g., motion detected) + // Check this AFTER HoldTime expiration so new detections can immediately re-trigger occupancy + if (millis() - lastDetectionEvent > detectionInterval) { + // New detection event occurred + lastDetectionEvent = millis(); + + if (!occupancyState) { + // Transition from unoccupied to occupied - start hold timer + occupancyState = true; + lastDetectionTime = millis(); + Serial.printf("Occupancy detected! Holding state for %u seconds (HoldTime)\n", OccupancySensor.getHoldTime()); + } else { + // Already occupied - new detection extends the hold period by resetting the timer + // This simulates continuous occupancy (person still present) + lastDetectionTime = millis(); + Serial.printf("Occupancy still detected. Resetting hold timer to %u seconds (HoldTime)\n", OccupancySensor.getHoldTime()); + } + } + + return occupancyState; +} + +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 Preferences and read stored HoldTime value + matterPref.begin("MatterPrefs", false); + uint16_t storedHoldTime = matterPref.getUShort(holdTimePrefKey, HOLD_TIME_DEFAULT); + + // Validate stored value is within limits + if (storedHoldTime < HOLD_TIME_MIN || storedHoldTime > HOLD_TIME_MAX) { + uint16_t invalidValue = storedHoldTime; + storedHoldTime = HOLD_TIME_DEFAULT; + Serial.printf("Invalid stored HoldTime (%u), using default: %u seconds\n", invalidValue, HOLD_TIME_DEFAULT); + } else if (storedHoldTime != HOLD_TIME_DEFAULT) { + Serial.printf("Restored HoldTime from Preferences: %u seconds\n", storedHoldTime); + } + + // Register callback for HoldTime changes from Matter Controller + OccupancySensor.onHoldTimeChange([](uint16_t holdTime_seconds) -> bool { + Serial.printf("HoldTime changed to %u seconds by Matter Controller\n", holdTime_seconds); + // Store the new HoldTime value to Preferences for persistence across reboots + matterPref.putUShort(holdTimePrefKey, holdTime_seconds); + // The callback can return false to reject the change, or true to accept it + // In this case, we always accept the change and update the simulator + return true; + }); + + // set initial occupancy sensor state as false and connected to a PIR sensor type (default) + OccupancySensor.begin(); + + // Matter beginning - Last step, after all EndPoints are initialized + Matter.begin(); + + // Set HoldTimeLimits after Matter.begin() (optional, but recommended for validation) + if (!OccupancySensor.setHoldTimeLimits(HOLD_TIME_MIN, HOLD_TIME_MAX, HOLD_TIME_DEFAULT)) { + Serial.println("Warning: Failed to set HoldTimeLimits"); + } else { + Serial.printf("HoldTimeLimits set: Min=%u, Max=%u, Default=%u seconds\n", HOLD_TIME_MIN, HOLD_TIME_MAX, HOLD_TIME_DEFAULT); + } + + // Set initial HoldTime (use stored value if valid, otherwise use default) + // This must be done after Matter.begin() because setHoldTime() requires the Matter event loop + if (!OccupancySensor.setHoldTime(storedHoldTime)) { + Serial.printf("Warning: Failed to set HoldTime to %u seconds\n", storedHoldTime); + } else { + Serial.printf("HoldTime set to: %u seconds\n", storedHoldTime); + } + + Serial.printf("Initial HoldTime: %u seconds\n", OccupancySensor.getHoldTime()); + + // 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 Occupancy 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."); + } +} + +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. + } + + if (button_state && digitalRead(buttonPin) == HIGH) { + 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 Occupancy Sensor Matter Accessory. It shall be commissioned again."); + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so + } + + // Check Simulated Occupancy Sensor and set Matter Attribute + OccupancySensor.setOccupancy(simulatedHWOccupancySensor()); + + delay(50); +} diff --git a/libraries/Matter/examples/MatterOccupancyWithHoldTime/README.md b/libraries/Matter/examples/MatterOccupancyWithHoldTime/README.md new file mode 100644 index 00000000000..d75d0bbd0d4 --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancyWithHoldTime/README.md @@ -0,0 +1,329 @@ +# Matter Occupancy Sensor with HoldTime Example + +This example demonstrates how to create a Matter-compatible occupancy sensor device with HoldTime functionality using an ESP32 SoC microcontroller.\ +The application showcases Matter commissioning, sensor data reporting to smart home ecosystems, automatic simulation of occupancy state changes, and HoldTime configuration with persistence across reboots. + +## 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 with HoldTime support +- Support for both Wi-Fi and Thread(*) connectivity +- Occupancy state reporting (Occupied/Unoccupied) +- HoldTime attribute for configuring how long the sensor holds the "occupied" state +- HoldTimeLimits (min, max, default) for validation and controller guidance +- HoldTime persistence across reboots using Preferences (NVS) +- Automatic simulation of occupancy state changes every 2 minutes with HoldTime expiration +- HoldTime change callback for real-time updates from Matter controllers +- 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. **HoldTime configuration** (optional): + The example uses default HoldTime limits. You can customize them: + ```cpp + const uint16_t HOLD_TIME_MIN = 0; // Minimum HoldTime in seconds + const uint16_t HOLD_TIME_MAX = 3600; // Maximum HoldTime in seconds (1 hour) + const uint16_t HOLD_TIME_DEFAULT = 30; // Default HoldTime in seconds + ``` + +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 + ``` + +4. **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 `MatterOccupancyWithHoldTime.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. + +3. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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 + +Restored HoldTime from Preferences: 30 seconds +HoldTimeLimits set: Min=0, Max=3600, Default=30 seconds +HoldTime set to: 30 seconds +Initial HoldTime: 30 seconds + +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. +Occupancy detected! Holding state for 30 seconds (HoldTime) +HoldTime expired. Switching to unoccupied state. +``` + +After commissioning, the occupancy sensor will automatically simulate occupancy detections every 2 minutes. When occupancy is detected, the sensor holds the "occupied" state for the configured HoldTime duration (default: 30 seconds). After HoldTime expires, it automatically switches to "unoccupied" state. The Matter controller will receive these state updates and can also configure the HoldTime value. + +## 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 with HoldTime support that: + +- Starts in the unoccupied state (false) +- Simulates occupancy detection every 2 minutes +- When occupancy is detected, holds the "occupied" state for the HoldTime duration +- After HoldTime expires, automatically switches to "unoccupied" state +- If new detections occur while occupied, the HoldTime timer resets (continuous occupancy) +- Updates the Matter attribute automatically + +**HoldTime Behavior:** +- The HoldTime value determines how long the sensor maintains the "occupied" state after the last detection +- HoldTime can be configured via Matter Controller (within the min/max limits) +- HoldTime value is persisted to Preferences and restored on reboot +- When HoldTime expires, the sensor transitions to "unoccupied" even if no new detection occurs + +To use a real occupancy sensor, replace the `simulatedHWOccupancySensor()` function with your sensor library code. The HoldTime functionality will work the same way - the sensor will hold the occupied state for the configured HoldTime duration after motion is no longer detected. + +### 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) +8. You can configure the HoldTime value through the device settings (if supported by your Home app version) + +#### 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 MatterOccupancyWithHoldTime example consists of the following main components: + +1. **`setup()`**: + - Initializes hardware (button), configures Wi-Fi (if needed) + - Initializes Preferences and restores stored HoldTime value + - Registers HoldTime change callback for persistence + - Sets up the Matter Occupancy Sensor endpoint with initial state (unoccupied) + - Calls `Matter.begin()` to start the Matter stack + - Sets HoldTimeLimits (min, max, default) after Matter.begin() + - Sets initial HoldTime value (from Preferences or default) + - Waits for Matter commissioning + +2. **`loop()`**: + - Handles button input for factory reset + - Continuously checks the simulated occupancy sensor and updates the Matter attribute + - Allows the Matter stack to process events + +3. **`simulatedHWOccupancySensor()`**: + - Simulates a hardware occupancy sensor with HoldTime support + - Detects occupancy every 2 minutes + - Holds the "occupied" state for HoldTime seconds after detection + - Automatically transitions to "unoccupied" when HoldTime expires + - Resets HoldTime timer on new detections while occupied (continuous occupancy) + - Replace this function with your actual sensor reading code + +4. **HoldTime Callback**: + - `onHoldTimeChange()` callback persists HoldTime changes to Preferences + - Ensures HoldTime value is maintained across device reboots + +## 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 detects occupancy every 2 minutes (120000 ms). The state will hold for HoldTime seconds after detection. If you're using a real sensor, ensure it's properly connected and reading correctly +- **HoldTime not persisting**: Verify that Preferences is properly initialized and the callback is saving the value. Check Serial Monitor for "HoldTime changed" messages +- **HoldTime not working**: Ensure `setHoldTimeLimits()` and `setHoldTime()` are called after `Matter.begin()`. Check Serial Monitor for error messages +- **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/MatterOccupancyWithHoldTime/ci.yml b/libraries/Matter/examples/MatterOccupancyWithHoldTime/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterOccupancyWithHoldTime/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..effc8e699a5 --- /dev/null +++ b/libraries/Matter/examples/MatterOnIdentify/README.md @@ -0,0 +1,223 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..a57aef9bdb7 --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffLight/README.md @@ -0,0 +1,191 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..c36d6b2e382 --- /dev/null +++ b/libraries/Matter/examples/MatterOnOffPlugin/README.md @@ -0,0 +1,208 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..3a03fd7c50a --- /dev/null +++ b/libraries/Matter/examples/MatterPressureSensor/README.md @@ -0,0 +1,201 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..e3a08d0a14f --- /dev/null +++ b/libraries/Matter/examples/MatterRainSensor/README.md @@ -0,0 +1,185 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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/MatterSimpleBlinds/MatterSimpleBlinds.ino b/libraries/Matter/examples/MatterSimpleBlinds/MatterSimpleBlinds.ino new file mode 100644 index 00000000000..a2f21d1e9aa --- /dev/null +++ b/libraries/Matter/examples/MatterSimpleBlinds/MatterSimpleBlinds.ino @@ -0,0 +1,92 @@ +// 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 Simple Blinds Example +// This is a minimal example that only controls Lift percentage using a single onGoToLiftPercentage() callback + +#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 +// Window Covering Endpoint +MatterWindowCovering WindowBlinds; + +// 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 + +// Simple callback - handles window Lift change request +bool onBlindsLift(uint8_t liftPercent) { + // This example only uses lift + Serial.printf("Window Covering change request: Lift=%d%%\r\n", liftPercent); + + // Returning true will store the new Lift value into the Matter Cluster + return true; +} + +void setup() { + Serial.begin(115200); + delay(1000); + Serial.println("\n============================"); + Serial.println("Matter Simple Blinds Example"); + Serial.println("============================\n"); + +// 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); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +#endif + + // Initialize Window Covering endpoint + // Using ROLLERSHADE type (lift only, no tilt) + WindowBlinds.begin(100, 0, MatterWindowCovering::ROLLERSHADE); + + // Set up the onGoToLiftPercentage callback - this handles all window covering changes requested by the Matter Controller + WindowBlinds.onGoToLiftPercentage(onBlindsLift); + + // Start Matter + Matter.begin(); + Serial.println("Matter started"); + Serial.println(); + + // Print commissioning information + 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()); + Serial.println("========================================"); +} + +void loop() { + delay(100); +} diff --git a/libraries/Matter/examples/MatterSimpleBlinds/README.md b/libraries/Matter/examples/MatterSimpleBlinds/README.md new file mode 100644 index 00000000000..ab3b5d0765c --- /dev/null +++ b/libraries/Matter/examples/MatterSimpleBlinds/README.md @@ -0,0 +1,146 @@ +# Matter Simple Blinds Example + +This is a minimal example demonstrating how to create a Matter-compatible window covering device with lift control only. This example uses a single `onGoToLiftPercentage()` callback to handle all window covering lift changes, making it ideal for simple implementations. + +## 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 window covering device +- **Lift control only** (0-100%) - simplified implementation +- **Single `onGoToLiftPercentage()` callback** - handles all window covering lift changes when `TargetPositionLiftPercent100ths` changes +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- Window covering motor/actuator (optional for testing - example simulates movement) + +## 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** (for ESP32 and ESP32-S2 only): + ```cpp + const char *ssid = "your-ssid"; + const char *password = "your-password"; + ``` + +## Building and Flashing + +1. Open the `MatterSimpleBlinds.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. + +3. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. Click the **Upload** button to compile and flash the sketch. + +## Expected Output + +``` +============================ +Matter Simple Blinds Example +============================ + +Connecting to your-ssid +WiFi connected +IP address: 192.168.1.100 +Matter started + +======================================== +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:Y.K9042C00KA0648G00 +======================================== +``` + +When a command is received from the Matter controller: +``` +Window Covering change request: Lift=50% +``` + +## Usage + +1. **Commissioning**: Use the QR code or manual pairing code to commission the device to your Matter hub (Apple Home, Google Home, or Amazon Alexa). + +2. **Control**: Once commissioned, you can control the window covering lift percentage (0-100%) from your smart home app. The `onGoToLiftPercentage()` callback will be triggered whenever the target lift percentage changes. + +## Code Structure + +- **`onBlindsLift()`**: Callback function that handles window covering lift changes. This is registered with `WindowBlinds.onGoToLiftPercentage()` and is triggered when `TargetPositionLiftPercent100ths` changes. The callback receives the target lift percentage (0-100%). +- **`setup()`**: Initializes Wi-Fi (if needed), Window Covering endpoint with `ROLLERSHADE` type, registers the callback, and starts Matter. +- **`loop()`**: Empty - all control is handled via Matter callbacks. + +## Customization + +### Adding Motor Control + +In the `onBlindsLift()` callback, replace the simulation code with actual motor control: + +```cpp +bool onBlindsLift(uint8_t liftPercent) { + Serial.printf("Moving window covering to %d%%\r\n", liftPercent); + + // Here you would control your actual motor/actuator + // For example: + // - Calculate target position based on liftPercent and installed limits (if configured) + // - Move motor to target position + // - When movement is complete, update current position: + // WindowBlinds.setLiftPercentage(finalLiftPercent); + // WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL); + + // For this minimal example, we just return true to accept the command + return true; // Indicate command was accepted +} +``` + +## Troubleshooting + +1. **Device not discoverable**: Ensure Wi-Fi is connected (for ESP32/ESP32-S2) or BLE is enabled (for other chips). + +2. **Lift percentage not updating**: Check that `onGoToLiftPercentage()` callback is properly registered and that `setLiftPercentage()` is called when movement is complete to update the `CurrentPosition` attribute. + +3. **Commands not working**: Ensure the callback returns `true` to accept the command. If it returns `false`, the command will be rejected. + +4. **Motor not responding**: Replace the simulation code in `onBlindsLift()` with your actual motor control implementation. Remember to update `CurrentPosition` and set `OperationalState` to `STALL` when movement is complete. + +## Notes + +- This example uses `ROLLERSHADE` window covering type (lift only, no tilt). +- The example accepts commands but doesn't actually move a motor. In a real implementation, you should: + 1. Move the motor to the target position in the callback + 2. Update `CurrentPositionLiftPercent100ths` using `setLiftPercentage()` when movement is complete + 3. Set `OperationalState` to `STALL` using `setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL)` to indicate the device has reached the target position +- **Important**: `onGoToLiftPercentage()` is called when `TargetPositionLiftPercent100ths` changes. This happens when commands are executed or when a Matter controller writes directly to the target position attribute. +- Commands modify `TargetPosition`, not `CurrentPosition`. The application is responsible for updating `CurrentPosition` when the physical device actually moves. diff --git a/libraries/Matter/examples/MatterSimpleBlinds/ci.yml b/libraries/Matter/examples/MatterSimpleBlinds/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterSimpleBlinds/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..10e443d61ba --- /dev/null +++ b/libraries/Matter/examples/MatterSmartButton/README.md @@ -0,0 +1,185 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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/MatterStatus/MatterStatus.ino b/libraries/Matter/examples/MatterStatus/MatterStatus.ino new file mode 100644 index 00000000000..ffbb5fa6212 --- /dev/null +++ b/libraries/Matter/examples/MatterStatus/MatterStatus.ino @@ -0,0 +1,119 @@ +// 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 Status Example +// This example demonstrates how to check enabled Matter features and connectivity status +// It implements a basic on/off light and reports capability and connection status + +#include +// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network +#if !CONFIG_ENABLE_CHIPOBLE // ESP32 and ESP32-S2 do not support BLE commissioning +// if the device can be commissioned using BLE, WiFi is not used - save flash space +#include +// 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 + +// List of Matter Endpoints for this Node +// On/Off Light Endpoint +MatterOnOffLight OnOffLight; + +// set your board LED pin here +#ifdef LED_BUILTIN +const uint8_t ledPin = LED_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 LED pin" +#endif + +// Matter Protocol Endpoint Callback +bool setLightOnOff(bool state) { + Serial.printf("User Callback :: New Light State = %s\r\n", state ? "ON" : "OFF"); + if (state) { + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + } + // This callback must return the success state to Matter core + return true; +} + +void setup() { + // Initialize the LED (light) GPIO + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, LOW); // Start with light OFF + + Serial.begin(115200); + delay(1000); + Serial.println("\n========================================"); + Serial.println("Matter Status Example"); + Serial.println("========================================\n"); + + // Report enabled features + Serial.println("=== Enabled Features ==="); + Serial.printf("WiFi Station Enabled: %s\r\n", Matter.isWiFiStationEnabled() ? "YES" : "NO"); + Serial.printf("WiFi Access Point Enabled: %s\r\n", Matter.isWiFiAccessPointEnabled() ? "YES" : "NO"); + Serial.printf("Thread Enabled: %s\r\n", Matter.isThreadEnabled() ? "YES" : "NO"); + Serial.printf("BLE Commissioning Enabled: %s\r\n", Matter.isBLECommissioningEnabled() ? "YES" : "NO"); + Serial.println(); + +// 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 + if (Matter.isWiFiStationEnabled()) { + Serial.print("Connecting to "); + Serial.println(ssid); + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + } +#endif + + // Initialize On/Off Light endpoint + OnOffLight.begin(false); // Start with light OFF + OnOffLight.onChange(setLightOnOff); + + // Start Matter + Matter.begin(); + Serial.println("Matter started"); + Serial.println(); + + // Print commissioning information + 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()); + Serial.println("========================================\n"); +} + +void loop() { + // Report connection status every 10 seconds + Serial.println("=== Connection Status ==="); + Serial.printf("WiFi Connected: %s\r\n", Matter.isWiFiConnected() ? "YES" : "NO"); + Serial.printf("Thread Connected: %s\r\n", Matter.isThreadConnected() ? "YES" : "NO"); + Serial.printf("Device Connected: %s\r\n", Matter.isDeviceConnected() ? "YES" : "NO"); + Serial.printf("Device Commissioned: %s\r\n", Matter.isDeviceCommissioned() ? "YES" : "NO"); + Serial.println(); + delay(10000); +} diff --git a/libraries/Matter/examples/MatterStatus/README.md b/libraries/Matter/examples/MatterStatus/README.md new file mode 100644 index 00000000000..4c482f22792 --- /dev/null +++ b/libraries/Matter/examples/MatterStatus/README.md @@ -0,0 +1,204 @@ +# Matter Status Example + +This example demonstrates how to check enabled Matter features and connectivity status using the Matter library's capability query functions. It implements a basic on/off light device and periodically reports the status of enabled features and network connections. + +## 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 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 an on/off light device +- **Capability reporting**: Checks and reports enabled Matter features at startup + - `isWiFiStationEnabled()`: Checks if Wi-Fi Station mode is supported and enabled + - `isWiFiAccessPointEnabled()`: Checks if Wi-Fi AP mode is supported and enabled + - `isThreadEnabled()`: Checks if Thread network is supported and enabled + - `isBLECommissioningEnabled()`: Checks if BLE commissioning is supported and enabled +- **Connection status monitoring**: Reports connection status every 10 seconds + - `isWiFiConnected()`: Checks Wi-Fi connection status (if Wi-Fi Station is enabled) + - `isThreadConnected()`: Checks Thread connection status (if Thread is enabled) + - `isDeviceConnected()`: Checks overall device connectivity (Wi-Fi or Thread) + - `isDeviceCommissioned()`: Checks if the device is commissioned to a Matter fabric +- Simple on/off light control +- Matter commissioning via QR code or manual pairing code +- Integration with Apple HomeKit, Amazon Alexa, and Google Home + +## Hardware Requirements + +- ESP32 compatible development board (see supported targets table) +- LED connected to GPIO pin (or using built-in LED) for visual feedback + +## Pin Configuration + +- **LED**: Uses `LED_BUILTIN` if defined, otherwise pin 2 + +## 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** (for ESP32 and ESP32-S2 only): + ```cpp + const char *ssid = "your-ssid"; + const char *password = "your-password"; + ``` + +2. **LED pin configuration** (if not using built-in LED): + ```cpp + const uint8_t ledPin = 2; // Set your LED pin here + ``` + +## Building and Flashing + +1. Open the `MatterStatus.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. + +3. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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: + +``` +======================================== +Matter Status Example +======================================== + +=== Enabled Features === +WiFi Station Enabled: YES +WiFi Access Point Enabled: NO +Thread Enabled: NO +BLE Commissioning Enabled: NO + +Connecting to your-ssid +....... +WiFi connected +IP address: 192.168.1.100 +Matter started + +======================================== +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:Y.K9042C00KA0648G00 +======================================== + +=== Connection Status === +WiFi Connected: YES +Thread Connected: NO +Device Connected: YES +Device Commissioned: NO + +=== Connection Status === +WiFi Connected: YES +Thread Connected: NO +Device Connected: YES +Device Commissioned: NO + +... (reports every 10 seconds) + +User Callback :: New Light State = ON +=== Connection Status === +WiFi Connected: YES +Thread Connected: NO +Device Connected: YES +Device Commissioned: YES + +... (reports every 10 seconds) +``` + +## Usage + +### Capability Queries + +The example demonstrates the use of capability query functions that check both hardware support (SoC capabilities) and Matter configuration: + +- **`Matter.isWiFiStationEnabled()`**: Returns `true` if the device supports Wi-Fi Station mode and it's enabled in Matter configuration +- **`Matter.isWiFiAccessPointEnabled()`**: Returns `true` if the device supports Wi-Fi AP mode and it's enabled in Matter configuration +- **`Matter.isThreadEnabled()`**: Returns `true` if the device supports Thread networking and it's enabled in Matter configuration +- **`Matter.isBLECommissioningEnabled()`**: Returns `true` if the device supports BLE and BLE commissioning is enabled + +These functions are useful for: +- Determining which features are available on the current device +- Adapting application behavior based on available capabilities +- Debugging configuration issues + +### Connection Status Monitoring + +The example periodically reports connection status every 10 seconds: + +- **`Matter.isWiFiConnected()`**: Returns `true` if Wi-Fi Station is connected. If Wi-Fi Station is not enabled, always returns `false`. +- **`Matter.isThreadConnected()`**: Returns `true` if Thread is attached to a network. If Thread is not enabled, always returns `false`. +- **`Matter.isDeviceConnected()`**: Returns `true` if the device is connected via Wi-Fi or Thread (overall connectivity status) +- **`Matter.isDeviceCommissioned()`**: Returns `true` if the device has been commissioned to a Matter fabric + +### Smart Home Integration + +Use a Matter-compatible hub (like an Apple HomePod, Google Nest Hub, or Amazon Echo) to commission the device. Once commissioned, you can control the light from your smart home app. + +## Code Structure + +- **`setup()`**: + - Initializes hardware (LED) + - Reports enabled features using capability query functions + - Connects to Wi-Fi (if needed and enabled) + - Initializes On/Off Light endpoint + - Starts Matter stack + - Prints commissioning information + +- **`loop()`**: + - Reports connection status every 10 seconds + - All light control is handled via Matter callbacks + +- **Callbacks**: + - `setLightOnOff()`: Controls the physical LED based on the on/off state and prints the state change to Serial Monitor + +## Troubleshooting + +1. **Device not discoverable**: Ensure Wi-Fi is connected (for ESP32/ESP32-S2) or BLE is enabled (for other chips). + +2. **Capability queries return unexpected values**: These functions check both hardware support and Matter configuration. Verify that the features are enabled in your Matter build configuration. + +3. **Connection status not updating**: The status is reported every 10 seconds. Check Serial Monitor output to see the periodic reports. + +4. **LED not responding**: Verify pin configurations and connections. + +5. **Failed to commission**: Try factory resetting the device by calling `Matter.decommission()`. 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` + +## 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/MatterStatus/ci.yml b/libraries/Matter/examples/MatterStatus/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterStatus/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..64135190bcc --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinet/README.md @@ -0,0 +1,219 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..398548f60d1 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureControlledCabinetLevels/README.md @@ -0,0 +1,219 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..2a4ce7bd2e6 --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureLight/README.md @@ -0,0 +1,217 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..c62e656a68e --- /dev/null +++ b/libraries/Matter/examples/MatterTemperatureSensor/README.md @@ -0,0 +1,216 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..32a61ce86c8 --- /dev/null +++ b/libraries/Matter/examples/MatterThermostat/README.md @@ -0,0 +1,240 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..9ec4c71b4fa --- /dev/null +++ b/libraries/Matter/examples/MatterWaterFreezeDetector/README.md @@ -0,0 +1,185 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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..17342ab4ced --- /dev/null +++ b/libraries/Matter/examples/MatterWaterLeakDetector/README.md @@ -0,0 +1,185 @@ +# 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. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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/examples/MatterWindowCovering/MatterWindowCovering.ino b/libraries/Matter/examples/MatterWindowCovering/MatterWindowCovering.ino new file mode 100644 index 00000000000..c63915a7fdf --- /dev/null +++ b/libraries/Matter/examples/MatterWindowCovering/MatterWindowCovering.ino @@ -0,0 +1,355 @@ +// 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 +// Window Covering Endpoint +MatterWindowCovering WindowBlinds; + +// 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 Lift & Tilt state stored, using Preferences +Preferences matterPref; +const char *liftPercentPrefKey = "LiftPercent"; +const char *tiltPercentPrefKey = "TiltPercent"; + +// 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 + +// Window covering limits +// Lift limits in centimeters (physical position) +const uint16_t MAX_LIFT = 200; // Maximum lift position (fully open) +const uint16_t MIN_LIFT = 0; // Minimum lift position (fully closed) + +// Tilt limits (absolute values for conversion, not physical units) +// Tilt is a rotation, not a linear measurement +const uint16_t MAX_TILT = 90; // Maximum tilt absolute value +const uint16_t MIN_TILT = 0; // Minimum tilt absolute value + +// Current window covering state +// These will be initialized in setup() based on installed limits and saved percentages +uint16_t currentLift = 0; // Lift position in cm +uint8_t currentLiftPercent = 100; +uint8_t currentTiltPercent = 0; // Tilt rotation percentage (0-100%) + +// Visualize window covering position using RGB LED +// Lift percentage controls brightness (0% = off, 100% = full brightness) +#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 RGB_BUILTIN +#warning "Do not forget to set the RGB LED pin" +#endif + +void visualizeWindowBlinds(uint8_t liftPercent, uint8_t tiltPercent) { +#ifdef RGB_BUILTIN + // Use RGB LED to visualize lift position (brightness) and tilt (color shift) + float brightness = (float)liftPercent / 100.0; // 0.0 to 1.0 + // Tilt affects color: 0% = red, 100% = blue + uint8_t red = (uint8_t)(map(tiltPercent, 0, 100, 255, 0) * brightness); + uint8_t blue = (uint8_t)(map(tiltPercent, 0, 100, 0, 255) * brightness); + uint8_t green = 0; + rgbLedWrite(ledPin, red, green, blue); +#else + // For non-RGB boards, just use brightness + uint8_t brightnessValue = map(liftPercent, 0, 100, 0, 255); + analogWrite(ledPin, brightnessValue); +#endif +} + +// Window Covering Callbacks +bool fullOpen() { + // This is where you would trigger your motor to go to full open state + // For simulation, we update instantly + uint16_t openLimit = WindowBlinds.getInstalledOpenLimitLift(); + currentLift = openLimit; + currentLiftPercent = 100; + Serial.printf("Opening window covering to full open (position: %d cm)\r\n", currentLift); + + // Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition) + WindowBlinds.setLiftPercentage(currentLiftPercent); + + // Set operational status to STALL when movement is complete + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL); + + // Store state + matterPref.putUChar(liftPercentPrefKey, currentLiftPercent); + + return true; +} + +bool fullClose() { + // This is where you would trigger your motor to go to full close state + // For simulation, we update instantly + uint16_t closedLimit = WindowBlinds.getInstalledClosedLimitLift(); + currentLift = closedLimit; + currentLiftPercent = 0; + Serial.printf("Closing window covering to full close (position: %d cm)\r\n", currentLift); + + // Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition) + WindowBlinds.setLiftPercentage(currentLiftPercent); + + // Set operational status to STALL when movement is complete + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL); + + // Store state + matterPref.putUChar(liftPercentPrefKey, currentLiftPercent); + + return true; +} + +bool goToLiftPercentage(uint8_t liftPercent) { + // update Lift operational state + if (liftPercent > currentLiftPercent) { + // Set operational status to OPEN + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::MOVING_UP_OR_OPEN); + } + if (liftPercent < currentLiftPercent) { + // Set operational status to CLOSE + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::MOVING_DOWN_OR_CLOSE); + } + + // This is where you would trigger your motor to go towards liftPercent + // For simulation, we update instantly + // Calculate absolute position based on installed limits + uint16_t openLimit = WindowBlinds.getInstalledOpenLimitLift(); + uint16_t closedLimit = WindowBlinds.getInstalledClosedLimitLift(); + + // Linear interpolation: 0% = openLimit, 100% = closedLimit + if (openLimit < closedLimit) { + currentLift = openLimit + ((closedLimit - openLimit) * liftPercent) / 100; + } else { + currentLift = openLimit - ((openLimit - closedLimit) * liftPercent) / 100; + } + currentLiftPercent = liftPercent; + Serial.printf("Moving lift to %d%% (position: %d cm)\r\n", currentLiftPercent, currentLift); + + // Update CurrentPosition to reflect actual position (setLiftPercentage now only updates CurrentPosition) + WindowBlinds.setLiftPercentage(currentLiftPercent); + + // Set operational status to STALL when movement is complete + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL); + + // Store state + matterPref.putUChar(liftPercentPrefKey, currentLiftPercent); + + return true; +} + +bool goToTiltPercentage(uint8_t tiltPercent) { + // update Tilt operational state + if (tiltPercent < currentTiltPercent) { + // Set operational status to OPEN + WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::MOVING_UP_OR_OPEN); + } + if (tiltPercent > currentTiltPercent) { + // Set operational status to CLOSE + WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::MOVING_DOWN_OR_CLOSE); + } + + // This is where you would trigger your motor to rotate the shade to tiltPercent + // For simulation, we update instantly + currentTiltPercent = tiltPercent; + Serial.printf("Rotating tilt to %d%%\r\n", currentTiltPercent); + + // Update CurrentPosition to reflect actual position + WindowBlinds.setTiltPercentage(currentTiltPercent); + + // Set operational status to STALL when movement is complete + WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::STALL); + + // Store state + matterPref.putUChar(tiltPercentPrefKey, currentTiltPercent); + + return true; +} + +bool stopMotor() { + // Motor can be stopped while moving cover toward current target + Serial.println("Stopping window covering motor"); + + // Update CurrentPosition to reflect actual position when stopped + // (setLiftPercentage and setTiltPercentage now only update CurrentPosition) + WindowBlinds.setLiftPercentage(currentLiftPercent); + WindowBlinds.setTiltPercentage(currentTiltPercent); + + // Set operational status to STALL for both lift and tilt + WindowBlinds.setOperationalState(MatterWindowCovering::LIFT, MatterWindowCovering::STALL); + WindowBlinds.setOperationalState(MatterWindowCovering::TILT, MatterWindowCovering::STALL); + + return true; +} + +void setup() { + // Initialize the USER BUTTON (Boot button) GPIO + pinMode(buttonPin, INPUT_PULLUP); + // Initialize the RGB LED GPIO + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, LOW); + + 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 lift percentage is 100% (fully open) if not stored before + uint8_t lastLiftPercent = matterPref.getUChar(liftPercentPrefKey, 100); + // default tilt percentage is 0% if not stored before + uint8_t lastTiltPercent = matterPref.getUChar(tiltPercentPrefKey, 0); + + // Initialize window covering with BLIND_LIFT_AND_TILT type + WindowBlinds.begin(lastLiftPercent, lastTiltPercent, MatterWindowCovering::BLIND_LIFT_AND_TILT); + + // Configure installed limits for lift and tilt + WindowBlinds.setInstalledOpenLimitLift(MIN_LIFT); + WindowBlinds.setInstalledClosedLimitLift(MAX_LIFT); + WindowBlinds.setInstalledOpenLimitTilt(MIN_TILT); + WindowBlinds.setInstalledClosedLimitTilt(MAX_TILT); + + // Initialize current positions based on percentages and installed limits + uint16_t openLimitLift = WindowBlinds.getInstalledOpenLimitLift(); + uint16_t closedLimitLift = WindowBlinds.getInstalledClosedLimitLift(); + currentLiftPercent = lastLiftPercent; + if (openLimitLift < closedLimitLift) { + currentLift = openLimitLift + ((closedLimitLift - openLimitLift) * lastLiftPercent) / 100; + } else { + currentLift = openLimitLift - ((openLimitLift - closedLimitLift) * lastLiftPercent) / 100; + } + + currentTiltPercent = lastTiltPercent; + + Serial.printf( + "Window Covering limits configured: Lift [%d-%d cm], Tilt [%d-%d]\r\n", WindowBlinds.getInstalledOpenLimitLift(), + WindowBlinds.getInstalledClosedLimitLift(), WindowBlinds.getInstalledOpenLimitTilt(), WindowBlinds.getInstalledClosedLimitTilt() + ); + Serial.printf("Initial positions: Lift=%d cm (%d%%), Tilt=%d%%\r\n", currentLift, currentLiftPercent, currentTiltPercent); + + // Set callback functions + WindowBlinds.onOpen(fullOpen); + WindowBlinds.onClose(fullClose); + WindowBlinds.onGoToLiftPercentage(goToLiftPercentage); + WindowBlinds.onGoToTiltPercentage(goToTiltPercentage); + WindowBlinds.onStop(stopMotor); + + // Generic callback for Lift or Tilt change + WindowBlinds.onChange([](uint8_t liftPercent, uint8_t tiltPercent) { + Serial.printf("Window Covering changed: Lift=%d%%, Tilt=%d%%\r\n", liftPercent, tiltPercent); + visualizeWindowBlinds(liftPercent, tiltPercent); + 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: Lift=%d%%, Tilt=%d%%\r\n", WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage()); + // Update visualization based on initial state + visualizeWindowBlinds(WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage()); + } +} + +void loop() { + // Check Matter Window Covering 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 Window Covering 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: Lift=%d%%, Tilt=%d%%\r\n", WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage()); + // Update visualization based on initial state + visualizeWindowBlinds(WindowBlinds.getLiftPercentage(), WindowBlinds.getTiltPercentage()); + Serial.println("Matter Node is commissioned and connected to the network. Ready for use."); + } + + // A button is also used to control the window covering + // 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 to manually change lift percentage or to decommission + uint32_t time_diff = millis() - button_time_stamp; + if (digitalRead(buttonPin) == HIGH && button_state && time_diff > debounceTime) { + // Button is released - cycle lift percentage by 20% + button_state = false; // released + uint8_t targetLiftPercent = currentLiftPercent; + // go to the closest next 20% or move 20% more + if ((targetLiftPercent % 20) != 0) { + targetLiftPercent = ((targetLiftPercent / 20) + 1) * 20; + } else { + targetLiftPercent += 20; + } + if (targetLiftPercent > 100) { + targetLiftPercent = 0; + } + Serial.printf("User button released. Setting lift to %d%%\r\n", targetLiftPercent); + WindowBlinds.setTargetLiftPercent100ths(targetLiftPercent * 100); + } + + // 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 Window Covering Matter Accessory. It shall be commissioned again."); + WindowBlinds.setLiftPercentage(0); // close the covering + Matter.decommission(); + button_time_stamp = millis(); // avoid running decommissioning again, reboot takes a second or so + } +} diff --git a/libraries/Matter/examples/MatterWindowCovering/README.md b/libraries/Matter/examples/MatterWindowCovering/README.md new file mode 100644 index 00000000000..278329f9cde --- /dev/null +++ b/libraries/Matter/examples/MatterWindowCovering/README.md @@ -0,0 +1,242 @@ +# Matter Window Covering Example + +This example demonstrates how to create a Matter-compatible window covering 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 | 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 window covering device +- Support for both Wi-Fi and Thread(*) connectivity +- Lift position and percentage control (0-100%) - Lift represents the physical position (centimeters) +- Tilt rotation and percentage control (0-100%) - Tilt represents rotation of the shade, not a linear measurement +- Multiple window covering types support +- State persistence using `Preferences` library +- Button control for manual lift adjustment and factory reset +- RGB LED visualization of lift (brightness) and tilt (color) positions +- Installed limit configuration for lift (cm) and tilt (absolute values) +- 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 for visualization (uses built-in RGB LED if available) +- User button for manual control (uses BOOT button by default) + +## Pin Configuration + +- **RGB LED**: Uses `RGB_BUILTIN` if defined (for visualization), otherwise pin 2. The LED brightness represents lift position (0% = off, 100% = full brightness), and color represents tilt rotation (red = 0%, blue = 100%). +- **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. **RGB 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 manual lift 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 `MatterWindowCovering.ino` sketch in the Arduino IDE. +2. Select your ESP32 board from the **Tools > Board** menu. + +3. Select **"Huge APP (3MB No OTA/1MB SPIFFS)"** from **Tools > Partition Scheme** menu. + +4. Enable **"Erase All Flash Before Sketch Upload"** option from **Tools** menu. +5. Connect your ESP32 board to your computer via USB. +6. 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 +....... +WiFi 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. +... +Initial state: Lift=100%, Tilt=0% +Matter Node is commissioned and connected to the network. Ready for use. +Window Covering changed: Lift=100%, Tilt=0% +Moving lift to 50% (position: 100 cm) +Window Covering changed: Lift=50%, Tilt=0% +``` + +## Using the Device + +### Manual Control + +The user button (BOOT button by default) provides manual control: + +- **Short press of the button**: Cycle lift percentage by 20% increments. If the current position is not a multiple of 20%, it will round up to the next multiple of 20%. Otherwise, it will add 20% (0% → 20% → 40% → ... → 100% → 0%) +- **Long press (>5 seconds)**: Factory reset the device (decommission) + +### State Persistence + +The device saves the last known lift and tilt percentages using the `Preferences` library. After a power cycle or restart: + +- The device will restore to the last saved lift and tilt percentages +- Default state is 100% lift (fully open) and 0% tilt if no previous state was saved +- The Matter controller will be notified of the restored state +- The RGB LED will reflect the restored state + +### RGB LED Visualization + +The RGB LED provides visual feedback: + +- **Brightness**: Represents lift position (0% = off, 100% = full brightness) +- **Color**: Represents tilt position (red = 0% tilt, blue = 100% tilt) +- For boards without RGB LED, only brightness is used + +### Window Covering Integration + +For production use with a motorized window covering: + +1. **Motor Control**: + - Connect your motor driver to your ESP32 + - Update the callback functions (`fullOpen()`, `fullClose()`, `goToLiftPercentage()`, `goToTiltPercentage()`, `stopMotor()`) to control your actual motor + - The example currently simulates movement instantly - replace with actual motor control code + +2. **Position Feedback**: + - Use encoders or limit switches to provide position feedback + - For lift: Update `currentLift` (cm) based on actual motor position + - For tilt: Update `currentTiltPercent` (rotation percentage) based on actual motor rotation + - **Important**: Call `setLiftPercentage()` and `setTiltPercentage()` in your `onGoToLiftPercentage()` or `onGoToTiltPercentage()` callbacks to update `CurrentPosition` attributes when the physical device actually moves. This will trigger the `onChange()` callback if registered. + - Call `setOperationalState(LIFT, STALL)` or `setOperationalState(TILT, STALL)` when movement is complete to indicate the device has reached the target position + - Configure installed limits using `setInstalledOpenLimitLift()`, `setInstalledClosedLimitLift()`, `setInstalledOpenLimitTilt()`, and `setInstalledClosedLimitTilt()` to define the physical range of your window covering + + **Callback Flow:** + - Matter command → `TargetPosition` changes → `onGoToLiftPercentage()`/`onGoToTiltPercentage()` called + - Your callback moves the motor → When movement completes, call `setLiftPercentage()`/`setTiltPercentage()` + - `setLiftPercentage()`/`setTiltPercentage()` update `CurrentPosition` → `onChange()` called (if registered) + +3. **Window Covering Type**: + - Pass the covering type to `begin()` to configure the appropriate type (e.g., `BLIND_LIFT_AND_TILT`, `ROLLERSHADE`, etc.) + - Different types support different features (lift only, tilt only, or both) + - The covering type must be specified during initialization to ensure the correct features are enabled + +### 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 window covering/blind in your Home app +7. You can control both lift and tilt positions using sliders + +#### 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 window covering will appear in your Alexa app +6. You can control positions using voice commands like "Alexa, set blinds 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 positions using voice commands or sliders in the app + +## Code Structure + +The MatterWindowCovering example consists of the following main components: + +1. **`setup()`**: Initializes hardware (button, RGB LED), configures Wi-Fi (if needed), initializes `Preferences` library, sets up the Matter window covering endpoint with the last saved state, registers callback functions, and starts the Matter stack. + +2. **`loop()`**: Checks the Matter commissioning state, handles button input for manual lift control and factory reset, and allows the Matter stack to process events. + +3. **Callbacks**: + + **Target Position Callbacks** (called when `TargetPosition` attributes change): + - `fullOpen()`: Registered with `onOpen()` - called when `UpOrOpen` command is received. Moves window covering to fully open (100% lift), calls `setLiftPercentage()` to update `CurrentPosition`, and sets operational state to `STALL` + - `fullClose()`: Registered with `onClose()` - called when `DownOrClose` command is received. Moves window covering to fully closed (0% lift), calls `setLiftPercentage()` to update `CurrentPosition`, and sets operational state to `STALL` + - `goToLiftPercentage()`: Registered with `onGoToLiftPercentage()` - called when `TargetPositionLiftPercent100ths` changes (from commands, `setTargetLiftPercent100ths()`, or direct attribute writes). Calculates absolute position (cm) based on installed limits, calls `setLiftPercentage()` to update `CurrentPosition`, and sets operational state to `STALL` when movement is complete + - `goToTiltPercentage()`: Registered with `onGoToTiltPercentage()` - called when `TargetPositionTiltPercent100ths` changes. Calls `setTiltPercentage()` to update `CurrentPosition`, and sets operational state to `STALL` when movement is complete + - `stopMotor()`: Registered with `onStop()` - called when `StopMotion` command is received. Stops any ongoing movement, calls `setLiftPercentage()` and `setTiltPercentage()` to update `CurrentPosition` for both, and sets operational state to `STALL` for both + + **Current Position Callback** (called when `CurrentPosition` attributes change): + - `onChange()`: Registered with `onChange()` - called when `CurrentPositionLiftPercent100ths` or `CurrentPositionTiltPercent100ths` change (after `setLiftPercentage()` or `setTiltPercentage()` are called). Updates RGB LED visualization to reflect current positions + + **Note:** The Target Position callbacks (`fullOpen()`, `fullClose()`, `goToLiftPercentage()`, `goToTiltPercentage()`, `stopMotor()`) call `setLiftPercentage()` or `setTiltPercentage()` to update the `CurrentPosition` attributes. This triggers the `onChange()` callback, which updates the visualization. + +## Troubleshooting + +- **Device not visible during commissioning**: Ensure Wi-Fi or Thread connectivity is properly configured +- **Window covering not responding**: Verify callback functions are properly implemented and motor control is working +- **Position not updating**: Check that `setLiftPercentage()` and `setTiltPercentage()` are being called with correct values +- **State not persisting**: Check that the `Preferences` library is properly initialized and that flash memory is not corrupted +- **RGB LED not working**: For RGB LED, verify the pin supports RGB LED control. For non-RGB boards, ensure the pin supports PWM (analogWrite) +- **Tilt not working**: Ensure the covering type supports tilt (e.g., `BLIND_LIFT_AND_TILT`, `SHUTTER`, or `BLIND_TILT_ONLY`) and that it is specified in `begin()` +- **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 Window Covering Endpoint](https://docs.espressif.com/projects/arduino-esp32/en/latest/matter/ep_window_covering.html) + +## License + +This example is licensed under the Apache License, Version 2.0. diff --git a/libraries/Matter/examples/MatterWindowCovering/ci.yml b/libraries/Matter/examples/MatterWindowCovering/ci.yml new file mode 100644 index 00000000000..050a80ff543 --- /dev/null +++ b/libraries/Matter/examples/MatterWindowCovering/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..11f58d7ea12 100644 --- a/libraries/Matter/keywords.txt +++ b/libraries/Matter/keywords.txt @@ -19,15 +19,29 @@ 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 +MatterWindowCovering KEYWORD1 ControlSequenceOfOperation_t KEYWORD1 ThermostatMode_t KEYWORD1 +WindowCoveringType_t KEYWORD1 +OperationalState_t KEYWORD1 +OperationalStatusField_t KEYWORD1 EndPointCB KEYWORD1 +EndPointOpenCB KEYWORD1 +EndPointCloseCB KEYWORD1 +EndPointLiftCB KEYWORD1 +EndPointTiltCB KEYWORD1 +EndPointStopCB KEYWORD1 EndPointHeatingSetpointCB KEYWORD1 EndPointCoolingSetpointCB KEYWORD1 EndPointTemperatureCB KEYWORD1 @@ -40,6 +54,8 @@ EndPointIdentifyCB KEYWORD1 matterEvent_t KEYWORD1 matterEventCB KEYWORD1 attrOperation_t KEYWORD1 +OccupancySensorType_t KEYWORD1 +HoldTimeChangeCB KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -49,10 +65,14 @@ begin KEYWORD2 end KEYWORD2 getManualPairingCode KEYWORD2 getOnboardingQRCodeUrl KEYWORD2 +isBLECommissioningEnabled KEYWORD2 isDeviceCommissioned KEYWORD2 -isWiFiConnected KEYWORD2 -isThreadConnected KEYWORD2 isDeviceConnected KEYWORD2 +isThreadConnected KEYWORD2 +isThreadEnabled KEYWORD2 +isWiFiAccessPointEnabled KEYWORD2 +isWiFiConnected KEYWORD2 +isWiFiStationEnabled KEYWORD2 decommission KEYWORD2 attributeChangeCB KEYWORD2 setOnOff KEYWORD2 @@ -60,6 +80,8 @@ getOnOff KEYWORD2 toggle KEYWORD2 setBrightness KEYWORD2 getBrightness KEYWORD2 +setLevel KEYWORD2 +getLevel KEYWORD2 setColorTemperature KEYWORD2 getColorTemperature KEYWORD2 setColorRGB KEYWORD2 @@ -86,14 +108,36 @@ 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 getOccupancy KEYWORD2 +setHoldTime KEYWORD2 +getHoldTime KEYWORD2 +setHoldTimeLimits KEYWORD2 +onHoldTimeChange KEYWORD2 getControlSequence KEYWORD2 getMinHeatSetpoint KEYWORD2 getMaxHeatSetpoint KEYWORD2 @@ -108,10 +152,40 @@ setCoolingHeatingSetpoints KEYWORD2 setLocalTemperature KEYWORD2 getLocalTemperature KEYWORD2 getThermostatModeString KEYWORD2 -onChangeMode KEYWORD2 onChangeLocalTemperature KEYWORD2 onChangeCoolingSetpoint KEYWORD2 onChangeHeatingSetpoint KEYWORD2 +setLiftPosition KEYWORD2 +getLiftPosition KEYWORD2 +setLiftPercentage KEYWORD2 +getLiftPercentage KEYWORD2 +setTargetLiftPercent100ths KEYWORD2 +getTargetLiftPercent100ths KEYWORD2 +setInstalledOpenLimitLift KEYWORD2 +getInstalledOpenLimitLift KEYWORD2 +setInstalledClosedLimitLift KEYWORD2 +getInstalledClosedLimitLift KEYWORD2 +setTiltPosition KEYWORD2 +getTiltPosition KEYWORD2 +setTiltPercentage KEYWORD2 +getTiltPercentage KEYWORD2 +setTargetTiltPercent100ths KEYWORD2 +getTargetTiltPercent100ths KEYWORD2 +setInstalledOpenLimitTilt KEYWORD2 +getInstalledOpenLimitTilt KEYWORD2 +setInstalledClosedLimitTilt KEYWORD2 +getInstalledClosedLimitTilt KEYWORD2 +setCoveringType KEYWORD2 +getCoveringType KEYWORD2 +setOperationalStatus KEYWORD2 +getOperationalStatus KEYWORD2 +setOperationalState KEYWORD2 +getOperationalState KEYWORD2 +onOpen KEYWORD2 +onClose KEYWORD2 +onGoToLiftPercentage KEYWORD2 +onGoToTiltPercentage KEYWORD2 +onStop KEYWORD2 onEvent KEYWORD2 setEndPointId KEYWORD2 getEndPointId KEYWORD2 @@ -189,3 +263,23 @@ MATTER_FABRIC_REMOVED LITERAL1 MATTER_FABRIC_COMMITTED LITERAL1 MATTER_FABRIC_UPDATED LITERAL1 MATTER_ESP32_PUBLIC_SPECIFIC_EVENT LITERAL1 +ROLLERSHADE LITERAL1 +ROLLERSHADE_2_MOTOR LITERAL1 +ROLLERSHADE_EXTERIOR LITERAL1 +ROLLERSHADE_EXTERIOR_2_MOTOR LITERAL1 +DRAPERY LITERAL1 +AWNING LITERAL1 +SHUTTER LITERAL1 +BLIND_TILT_ONLY LITERAL1 +BLIND_LIFT_AND_TILT LITERAL1 +PROJECTOR_SCREEN LITERAL1 +STALL LITERAL1 +MOVING_UP_OR_OPEN LITERAL1 +MOVING_DOWN_OR_CLOSE LITERAL1 +GLOBAL LITERAL1 +LIFT LITERAL1 +TILT LITERAL1 +OCCUPANCY_SENSOR_TYPE_PIR LITERAL1 +OCCUPANCY_SENSOR_TYPE_ULTRASONIC LITERAL1 +OCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONIC LITERAL1 +OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT LITERAL1 diff --git a/libraries/Matter/library.properties b/libraries/Matter/library.properties index 522e777f852..b95e4e09a10 100644 --- a/libraries/Matter/library.properties +++ b/libraries/Matter/library.properties @@ -1,5 +1,5 @@ name=Matter -version=3.3.2 +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.cpp b/libraries/Matter/src/Matter.cpp index 5ddacc1622c..151a9e8990e 100644 --- a/libraries/Matter/src/Matter.cpp +++ b/libraries/Matter/src/Matter.cpp @@ -175,31 +175,70 @@ void ArduinoMatter::begin() { } } -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD -bool ArduinoMatter::isThreadConnected() { - return false; // Thread Network TBD +// Network and Commissioning Capability Queries +bool ArduinoMatter::isWiFiStationEnabled() { + // Check hardware support (SOC capabilities) AND Matter configuration +#ifdef SOC_WIFI_SUPPORTED +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION + return true; +#else + return false; +#endif +#else + return false; +#endif +} + +bool ArduinoMatter::isWiFiAccessPointEnabled() { + // Check hardware support (SOC capabilities) AND Matter configuration +#ifdef SOC_WIFI_SUPPORTED +#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_AP + return true; +#else + return false; +#endif +#else + return false; +#endif +} + +bool ArduinoMatter::isThreadEnabled() { + // Check Matter configuration only +#if CONFIG_ENABLE_MATTER_OVER_THREAD || CHIP_DEVICE_CONFIG_ENABLE_THREAD + return true; +#else + return false; +#endif } + +bool ArduinoMatter::isBLECommissioningEnabled() { + // Check hardware support (SOC capabilities) AND Matter/ESP configuration + // BLE commissioning requires: SOC BLE support AND (CHIPoBLE or NimBLE enabled) +#ifdef SOC_BLE_SUPPORTED +#if CONFIG_ENABLE_CHIPOBLE + return true; +#else + return false; +#endif +#else + return false; #endif +} bool ArduinoMatter::isDeviceCommissioned() { return chip::Server::GetInstance().GetFabricTable().FabricCount() > 0; } -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION bool ArduinoMatter::isWiFiConnected() { return chip::DeviceLayer::ConnectivityMgr().IsWiFiStationConnected(); } -#endif + +bool ArduinoMatter::isThreadConnected() { + return chip::DeviceLayer::ConnectivityMgr().IsThreadAttached(); +} bool ArduinoMatter::isDeviceConnected() { - bool retCode = false; -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD - retCode |= ArduinoMatter::isThreadConnected(); -#endif -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION - retCode |= ArduinoMatter::isWiFiConnected(); -#endif - return retCode; + return ArduinoMatter::isWiFiConnected() || ArduinoMatter::isThreadConnected(); } void ArduinoMatter::decommission() { diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index 89360e81d4b..dc72017a7d3 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -27,12 +27,18 @@ #include #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 enum matterEvent_t { @@ -173,13 +179,17 @@ class ArduinoMatter { return String("https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT:Y.K9042C00KA0648G00"); } static void begin(); + + // Network and Commissioning Capability Queries + // These methods check both hardware support (SOC capabilities) and Matter configuration + static bool isWiFiStationEnabled(); // Check if WiFi Station mode is supported and enabled + static bool isWiFiAccessPointEnabled(); // Check if WiFi AP mode is supported and enabled + static bool isThreadEnabled(); // Check if Thread network is supported and enabled + static bool isBLECommissioningEnabled(); // Check if BLE commissioning is supported and enabled + static bool isDeviceCommissioned(); -#if CHIP_DEVICE_CONFIG_ENABLE_WIFI_STATION static bool isWiFiConnected(); -#endif -#if CHIP_DEVICE_CONFIG_ENABLE_THREAD static bool isThreadConnected(); -#endif static bool isDeviceConnected(); static void decommission(); @@ -187,17 +197,23 @@ 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; friend class MatterThermostat; + friend class MatterWindowCovering; protected: static void _init(); 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/MatterOccupancySensor.cpp b/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp index f91a02e14a9..ec84ea9b0a5 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp +++ b/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.cpp @@ -18,11 +18,153 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace esp_matter; using namespace esp_matter::endpoint; +using namespace esp_matter::cluster; +using namespace esp_matter::cluster::occupancy_sensing::attribute; using namespace chip::app::Clusters; +// Custom AttributeAccessInterface wrapper that intercepts HoldTime writes to call user callbacks +// This wraps the standard OccupancySensing::Instance to add callback support +class OccupancySensingAttrAccessWrapper : public chip::app::AttributeAccessInterface { +public: + OccupancySensingAttrAccessWrapper() + : chip::app::AttributeAccessInterface(chip::Optional::Missing(), OccupancySensing::Id), + mInstance(chip::BitMask(0)) {} + + CHIP_ERROR Init() { + // Register THIS wrapper instance, not the standard instance + // The wrapper will delegate reads/writes to mInstance internally + bool registered = chip::app::AttributeAccessInterfaceRegistry::Instance().Register(this); + if (!registered) { + log_e("Failed to register OccupancySensing AttributeAccessInterface (duplicate?)"); + return CHIP_ERROR_INCORRECT_STATE; + } + return CHIP_NO_ERROR; + } + + CHIP_ERROR Read(const chip::app::ConcreteReadAttributePath &aPath, chip::app::AttributeValueEncoder &aEncoder) override { + // Delegate reads to the standard instance + return mInstance.Read(aPath, aEncoder); + } + + CHIP_ERROR Write(const chip::app::ConcreteDataAttributePath &aPath, chip::app::AttributeValueDecoder &aDecoder) override { + // Intercept HoldTime writes to call user callbacks + if (aPath.mAttributeId == OccupancySensing::Attributes::HoldTime::Id) { + uint16_t newHoldTime; + CHIP_ERROR err = aDecoder.Decode(newHoldTime); + if (err != CHIP_NO_ERROR) { + return err; + } + + // Validate against HoldTimeLimits first (same as Instance::Write does) + OccupancySensing::Structs::HoldTimeLimitsStruct::Type *currHoldTimeLimits = OccupancySensing::GetHoldTimeLimitsForEndpoint(aPath.mEndpointId); + if (currHoldTimeLimits == nullptr) { + return CHIP_ERROR_INVALID_ARGUMENT; + } + if (newHoldTime < currHoldTimeLimits->holdTimeMin || newHoldTime > currHoldTimeLimits->holdTimeMax) { + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + + // Find the MatterOccupancySensor instance for this endpoint and call its callback + MatterOccupancySensor *sensor = FindOccupancySensorForEndpoint(aPath.mEndpointId); + if (sensor != nullptr) { + // Call the user callback if set (this allows rejection of the change) + if (sensor->_onHoldTimeChangeCB) { + if (!sensor->_onHoldTimeChangeCB(newHoldTime)) { + // User callback rejected the change + return CHIP_IM_GLOBAL_STATUS(ConstraintError); + } + } + } + + // Call SetHoldTime directly (same as Instance::Write does) + err = OccupancySensing::SetHoldTime(aPath.mEndpointId, newHoldTime); + if (err == CHIP_NO_ERROR && sensor != nullptr) { + // Update the internal value to keep it in sync + sensor->holdTime_seconds = newHoldTime; + } + return err; + } + + // For other attributes, delegate to the standard instance + return mInstance.Write(aPath, aDecoder); + } + +private: + OccupancySensing::Instance mInstance; + + // Helper to find MatterOccupancySensor instance for an endpoint + static MatterOccupancySensor *FindOccupancySensorForEndpoint(chip::EndpointId endpointId) { + // Get the endpoint's private data (set when creating the endpoint) + void *priv_data = esp_matter::endpoint::get_priv_data(endpointId); + if (priv_data == nullptr) { + return nullptr; + } + + MatterOccupancySensor *sensor = static_cast(priv_data); + // Verify it's actually a MatterOccupancySensor by checking if it's started + if (sensor != nullptr && sensor->started) { + return sensor; + } + + return nullptr; + } +}; + +// Static AttributeAccessInterface wrapper instance +// Registered once globally to handle all OccupancySensing endpoints +namespace { +static OccupancySensingAttrAccessWrapper sOccupancySensingAttrAccess; +static bool sOccupancySensingAttrAccessRegistered = false; +} // namespace + +// Static helper functions for Matter event loop operations +namespace { +void SetHoldTimeInEventLoop(uint16_t endpoint_id, uint16_t holdTime_seconds) { + CHIP_ERROR err = OccupancySensing::SetHoldTime(endpoint_id, holdTime_seconds); + if (err != CHIP_NO_ERROR) { + ChipLogError(NotSpecified, "Failed to set HoldTime: %" CHIP_ERROR_FORMAT, err.Format()); + } else { + ChipLogDetail(NotSpecified, "HoldTime set to %u seconds", holdTime_seconds); + } +} + +void SetHoldTimeLimitsInEventLoop(uint16_t endpoint_id, uint16_t min_seconds, uint16_t max_seconds, uint16_t default_seconds) { + OccupancySensing::Structs::HoldTimeLimitsStruct::Type holdTimeLimits; + holdTimeLimits.holdTimeMin = min_seconds; + holdTimeLimits.holdTimeMax = max_seconds; + holdTimeLimits.holdTimeDefault = default_seconds; + + CHIP_ERROR err = OccupancySensing::SetHoldTimeLimits(endpoint_id, holdTimeLimits); + if (err != CHIP_NO_ERROR) { + ChipLogError(NotSpecified, "Failed to set HoldTimeLimits: %" CHIP_ERROR_FORMAT, err.Format()); + } else { + ChipLogDetail(NotSpecified, "HoldTimeLimits set: Min=%u, Max=%u, Default=%u seconds", min_seconds, max_seconds, default_seconds); + } +} + +void SetHoldTimeLimitsAndHoldTimeInEventLoop( + uint16_t endpoint_id, uint16_t min_seconds, uint16_t max_seconds, uint16_t default_seconds, uint16_t holdTime_seconds +) { + // Set HoldTimeLimits first + SetHoldTimeLimitsInEventLoop(endpoint_id, min_seconds, max_seconds, default_seconds); + + // Then adjust HoldTime to be within the new limits + SetHoldTimeInEventLoop(endpoint_id, holdTime_seconds); +} +} // namespace + // clang-format off const uint8_t MatterOccupancySensor::occupancySensorTypeBitmap[4] = { MatterOccupancySensor::occupancySensorTypePir, @@ -33,17 +175,26 @@ const uint8_t MatterOccupancySensor::occupancySensorTypeBitmap[4] = { // clang-format on bool MatterOccupancySensor::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 Occupancy Sensor device has not begun."); return false; } - log_d("Occupancy Sensor Attr update callback: endpoint: %u, cluster: %u, attribute: %u, val: %u", endpoint_id, cluster_id, attribute_id, val->val.u32); - return ret; + log_d("Occupancy Sensor Attr update callback: endpoint: %u, cluster: %u, attribute: %u", endpoint_id, cluster_id, attribute_id); + + // Note: HoldTime writes are handled by OccupancySensingAttrAccessWrapper::Write() + // since HoldTime is MANAGED_INTERNALLY and doesn't go through the normal esp-matter callback path + + return true; +} + +void MatterOccupancySensor::onHoldTimeChange(HoldTimeChangeCB onHoldTimeChangeCB) { + _onHoldTimeChangeCB = onHoldTimeChangeCB; } -MatterOccupancySensor::MatterOccupancySensor() {} +MatterOccupancySensor::MatterOccupancySensor() { + // HoldTimeLimits must be set explicitly via setHoldTimeLimits() after Matter.begin() +} MatterOccupancySensor::~MatterOccupancySensor() { end(); @@ -52,24 +203,84 @@ MatterOccupancySensor::~MatterOccupancySensor() { bool MatterOccupancySensor::begin(bool _occupancyState, OccupancySensorType_t _occupancySensorType) { ArduinoMatter::_init(); + // Initial HoldTime value is 0 (can be set later via setHoldTime() or setHoldTimeLimits()) + holdTime_seconds = 0; if (getEndPointId() != 0) { log_e("Matter Occupancy Sensor with Endpoint Id %d device has already been created.", getEndPointId()); return false; } - occupancy_sensor::config_t occupancy_sensor_config; occupancy_sensor_config.occupancy_sensing.occupancy = _occupancyState; occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type = _occupancySensorType; occupancy_sensor_config.occupancy_sensing.occupancy_sensor_type_bitmap = occupancySensorTypeBitmap[_occupancySensorType]; + // Set features based on sensor type + // Available features: other, passive_infrared, ultrasonic, physical_contact, active_infrared, radar, rf_sensing, vision + using namespace esp_matter::cluster::occupancy_sensing::feature; + + switch (_occupancySensorType) { + case OCCUPANCY_SENSOR_TYPE_PIR: occupancy_sensor_config.occupancy_sensing.features = passive_infrared::get_id(); break; + case OCCUPANCY_SENSOR_TYPE_ULTRASONIC: occupancy_sensor_config.occupancy_sensing.features = ultrasonic::get_id(); break; + case OCCUPANCY_SENSOR_TYPE_PIR_AND_ULTRASONIC: + occupancy_sensor_config.occupancy_sensing.features = passive_infrared::get_id() | ultrasonic::get_id(); + break; + case OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT: occupancy_sensor_config.occupancy_sensing.features = physical_contact::get_id(); break; + default: + // For unknown types, use "other" feature + occupancy_sensor_config.occupancy_sensing.features = other::get_id(); + break; + } // endpoint handles can be used to add/modify clusters. endpoint_t *endpoint = occupancy_sensor::create(node::get(), &occupancy_sensor_config, ENDPOINT_FLAG_NONE, (void *)this); if (endpoint == nullptr) { log_e("Failed to create Occupancy Sensor endpoint"); return false; } - occupancyState = _occupancyState; setEndPointId(endpoint::get_id(endpoint)); + occupancyState = _occupancyState; + + // Register AttributeAccessInterface for OccupancySensing cluster if not already registered + // This enables HoldTime and HoldTimeLimits (MANAGED_INTERNALLY attributes) to be read + // via the server implementation instead of falling back to esp-matter's placeholder storage + if (!sOccupancySensingAttrAccessRegistered) { + CHIP_ERROR err = sOccupancySensingAttrAccess.Init(); + if (err == CHIP_NO_ERROR) { + sOccupancySensingAttrAccessRegistered = true; + } else { + log_e("Failed to register OccupancySensing AttributeAccessInterface: %" CHIP_ERROR_FORMAT, err.Format()); + } + } + + // Add HoldTime and HoldTimeLimits attributes to the occupancy sensing cluster + cluster_t *cluster = cluster::get(endpoint, OccupancySensing::Id); + if (cluster != nullptr) { + // Create HoldTime attribute first (HoldTimeLimits may depend on it) + attribute_t *hold_time_attr = create_hold_time(cluster, holdTime_seconds); + if (hold_time_attr == nullptr) { + log_e("Failed to create HoldTime attribute"); + // Continue anyway, as HoldTime is optional + } else { + log_d("HoldTime attribute created with value %u seconds", holdTime_seconds); + } + + // Create the HoldTimeLimits attribute + // Since this attribute is MANAGED_INTERNALLY, we pass NULL/0/0 and let the CHIP server manage the value + // The server will handle TLV encoding/decoding automatically via AttributeAccessInterface + // Note: HoldTimeLimits should only be created if HoldTime was successfully created + if (hold_time_attr != nullptr) { + attribute_t *hold_time_limits_attr = create_hold_time_limits(cluster, NULL, 0, 0); + if (hold_time_limits_attr == nullptr) { + log_e("Failed to create HoldTimeLimits attribute"); + } else { + log_d("HoldTimeLimits attribute created"); + } + } else { + log_w("Skipping HoldTimeLimits creation because HoldTime attribute creation failed"); + } + } else { + log_e("Failed to get Occupancy Sensing cluster"); + } + log_i("Occupancy Sensor created with endpoint_id %d", getEndPointId()); started = true; @@ -99,9 +310,7 @@ bool MatterOccupancySensor::setOccupancy(bool _occupancyState) { } if (occupancyVal.val.u8 != _occupancyState) { occupancyVal.val.u8 = _occupancyState; - bool ret; - ret = updateAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal); - if (!ret) { + if (!updateAttributeVal(OccupancySensing::Id, OccupancySensing::Attributes::Occupancy::Id, &occupancyVal)) { log_e("Failed to update Occupancy Sensor Attribute."); return false; } @@ -112,4 +321,137 @@ bool MatterOccupancySensor::setOccupancy(bool _occupancyState) { return true; } +bool MatterOccupancySensor::setHoldTime(uint16_t _holdTime_seconds) { + if (!started) { + log_e("Matter Occupancy Sensor device has not begun."); + return false; + } + + if (getEndPointId() == 0) { + log_e("Endpoint ID is not set"); + return false; + } + + // avoid processing if there was no change + if (holdTime_seconds == _holdTime_seconds) { + return true; + } + + // Validate against HoldTimeLimits if they are set (using member variables) + if (holdTimeMax_seconds > 0) { + // Limits are set, validate the new value + if (_holdTime_seconds < holdTimeMin_seconds) { + log_e("HoldTime (%u) is below minimum (%u seconds)", _holdTime_seconds, holdTimeMin_seconds); + return false; + } + if (_holdTime_seconds > holdTimeMax_seconds) { + log_e("HoldTime (%u) exceeds maximum (%u seconds)", _holdTime_seconds, holdTimeMax_seconds); + return false; + } + } + + // SetHoldTime() calls MatterReportingAttributeChangeCallback() which must be called + // from the Matter event loop context to avoid stack locking errors. + // Schedule the call on the Matter event loop using ScheduleLambda. + if (!chip::DeviceLayer::SystemLayer().IsInitialized()) { + log_e("SystemLayer is not initialized. Matter.begin() must be called before setHoldTime()."); + return false; + } + + uint16_t endpoint_id = getEndPointId(); + + CHIP_ERROR schedule_err = chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, holdTime = _holdTime_seconds]() { + SetHoldTimeInEventLoop(endpoint_id, holdTime); + }); + + if (schedule_err != CHIP_NO_ERROR) { + log_e("Failed to schedule HoldTime update: %" CHIP_ERROR_FORMAT, schedule_err.Format()); + return false; + } + + // Update member variable immediately + holdTime_seconds = _holdTime_seconds; + log_v("HoldTime scheduled for update with value %u seconds", _holdTime_seconds); + + return true; +} + +bool MatterOccupancySensor::setHoldTimeLimits(uint16_t _holdTimeMin_seconds, uint16_t _holdTimeMax_seconds, uint16_t _holdTimeDefault_seconds) { + if (!started) { + log_e("Matter Occupancy Sensor device has not begun."); + return false; + } + + if (getEndPointId() == 0) { + log_e("Endpoint ID is not set"); + return false; + } + + // Validate limits + if (_holdTimeMin_seconds > _holdTimeMax_seconds) { + log_e("HoldTimeMin (%u) must be <= HoldTimeMax (%u)", _holdTimeMin_seconds, _holdTimeMax_seconds); + return false; + } + + if (_holdTimeDefault_seconds < _holdTimeMin_seconds || _holdTimeDefault_seconds > _holdTimeMax_seconds) { + log_e("HoldTimeDefault (%u) must be between HoldTimeMin (%u) and HoldTimeMax (%u)", _holdTimeDefault_seconds, _holdTimeMin_seconds, _holdTimeMax_seconds); + return false; + } + + // SetHoldTimeLimits() calls MatterReportingAttributeChangeCallback() which must be called + // from the Matter event loop context to avoid stack locking errors. + // Schedule the call on the Matter event loop using ScheduleLambda. + // First check if the scheduler is available (Matter.begin() must have been called) + if (!chip::DeviceLayer::SystemLayer().IsInitialized()) { + log_e("SystemLayer is not initialized. Matter.begin() must be called before setHoldTimeLimits()."); + return false; + } + + // Update member variables immediately + holdTimeMin_seconds = _holdTimeMin_seconds; + holdTimeMax_seconds = _holdTimeMax_seconds; + holdTimeDefault_seconds = _holdTimeDefault_seconds; + + // Check if current HoldTime is outside the new limits and adjust if necessary + uint16_t adjustedHoldTime = holdTime_seconds; + bool holdTimeAdjusted = false; + + if (holdTime_seconds < _holdTimeMin_seconds) { + adjustedHoldTime = _holdTimeMin_seconds; + holdTimeAdjusted = true; + log_i("Current HoldTime (%u) is below new minimum (%u), adjusting to minimum", holdTime_seconds, _holdTimeMin_seconds); + } else if (holdTime_seconds > _holdTimeMax_seconds) { + adjustedHoldTime = _holdTimeMax_seconds; + holdTimeAdjusted = true; + log_i("Current HoldTime (%u) exceeds new maximum (%u), adjusting to maximum", holdTime_seconds, _holdTimeMax_seconds); + } + + uint16_t endpoint_id = getEndPointId(); + CHIP_ERROR schedule_err; + + if (holdTimeAdjusted) { + // Schedule both limits and HoldTime updates together + schedule_err = chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, min = _holdTimeMin_seconds, max = _holdTimeMax_seconds, + def = _holdTimeDefault_seconds, holdTime = adjustedHoldTime]() { + SetHoldTimeLimitsAndHoldTimeInEventLoop(endpoint_id, min, max, def, holdTime); + }); + holdTime_seconds = adjustedHoldTime; + } else { + // No adjustment needed, just schedule the limits update + schedule_err = + chip::DeviceLayer::SystemLayer().ScheduleLambda([endpoint_id, min = _holdTimeMin_seconds, max = _holdTimeMax_seconds, def = _holdTimeDefault_seconds]() { + SetHoldTimeLimitsInEventLoop(endpoint_id, min, max, def); + }); + } + + if (schedule_err != CHIP_NO_ERROR) { + log_e("Failed to schedule HoldTimeLimits update: %" CHIP_ERROR_FORMAT, schedule_err.Format()); + return false; + } + + log_v("HoldTimeLimits scheduled for update: Min=%u, Max=%u, Default=%u seconds", _holdTimeMin_seconds, _holdTimeMax_seconds, _holdTimeDefault_seconds); + + return true; +} + #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.h b/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.h index acfa7fec632..aceef50c349 100644 --- a/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.h +++ b/libraries/Matter/src/MatterEndpoints/MatterOccupancySensor.h @@ -19,10 +19,16 @@ #include #include #include +#include using namespace chip::app::Clusters::OccupancySensing; +// Forward declaration for friend class +class OccupancySensingAttrAccessWrapper; + class MatterOccupancySensor : public MatterEndPoint { + friend class OccupancySensingAttrAccessWrapper; + public: // Different Occupancy Sensor Types enum OccupancySensorType_t { @@ -32,9 +38,11 @@ class MatterOccupancySensor : public MatterEndPoint { OCCUPANCY_SENSOR_TYPE_PHYSICAL_CONTACT = (uint8_t)OccupancySensorTypeEnum::kPhysicalContact }; + // Constructor MatterOccupancySensor(); ~MatterOccupancySensor(); // begin Matter Occupancy Sensor endpoint with initial occupancy state and default PIR sensor type + // Note: Call setHoldTimeLimits() after Matter.begin() to configure HoldTimeLimits (optional) bool begin(bool _occupancyState = false, OccupancySensorType_t _occupancySensorType = OCCUPANCY_SENSOR_TYPE_PIR); // this will just stop processing Occupancy Sensor Matter events void end(); @@ -46,6 +54,20 @@ class MatterOccupancySensor : public MatterEndPoint { return occupancyState; } + // set the hold time (in seconds) + // Must be called after Matter.begin() has been called (requires Matter event loop to be running) + bool setHoldTime(uint16_t _holdTime_seconds); + // returns the hold time (in seconds) + uint16_t getHoldTime() { + return holdTime_seconds; + } + + // set the hold time limits (min, max, default in seconds) + // Must be called after Matter.begin() has been called (requires Matter event loop to be running) + // Note: holdTimeDefault_seconds is informational metadata for Matter controllers (recommended default value). + // It does NOT automatically set the HoldTime attribute - use setHoldTime() to set the actual value. + bool setHoldTimeLimits(uint16_t _holdTimeMin_seconds, uint16_t _holdTimeMax_seconds, uint16_t _holdTimeDefault_seconds); + // bool conversion operator void operator=(bool _occupancyState) { setOccupancy(_occupancyState); @@ -55,6 +77,12 @@ class MatterOccupancySensor : public MatterEndPoint { return getOccupancy(); } + // User callback for HoldTime attribute changes + using HoldTimeChangeCB = std::function; + + // Set callback for HoldTime changes (called when Matter Controller changes HoldTime) + void onHoldTimeChange(HoldTimeChangeCB onHoldTimeChangeCB); + // 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); @@ -69,5 +97,14 @@ class MatterOccupancySensor : public MatterEndPoint { bool started = false; bool occupancyState = false; + uint16_t holdTime_seconds = 0; + + // HoldTimeLimits settings (set via setHoldTimeLimits() after Matter.begin()) + uint16_t holdTimeMin_seconds = 0; + uint16_t holdTimeMax_seconds = 0; // 0 means no maximum, no limits enforced + uint16_t holdTimeDefault_seconds = 0; + + // User callback + HoldTimeChangeCB _onHoldTimeChangeCB = nullptr; }; #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/Matter/src/MatterEndpoints/MatterWindowCovering.cpp b/libraries/Matter/src/MatterEndpoints/MatterWindowCovering.cpp new file mode 100644 index 00000000000..fc293c5c2da --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWindowCovering.cpp @@ -0,0 +1,871 @@ +// 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 + +using namespace esp_matter; +using namespace esp_matter::endpoint; +using namespace esp_matter::cluster; +using namespace esp_matter::cluster::window_covering; +using namespace esp_matter::cluster::window_covering::command; +using namespace esp_matter::cluster::window_covering::feature; +using namespace chip::app::Clusters; + +MatterWindowCovering::MatterWindowCovering() {} + +MatterWindowCovering::~MatterWindowCovering() { + end(); +} + +bool MatterWindowCovering::begin(uint8_t liftPercent, uint8_t tiltPercent, WindowCoveringType_t _coveringType) { + ArduinoMatter::_init(); + + if (getEndPointId() != 0) { + log_e("Matter Window Covering with Endpoint Id %d device has already been created.", getEndPointId()); + return false; + } + + coveringType = (_coveringType == 0) ? ROLLERSHADE : _coveringType; + + window_covering_device::config_t window_covering_config(0); + window_covering_config.window_covering.type = (uint8_t)coveringType; + window_covering_config.window_covering.config_status = 0; + window_covering_config.window_covering.operational_status = 0; + + currentLiftPercent = liftPercent; + currentTiltPercent = tiltPercent; + currentLiftPosition = 0; + currentTiltPosition = 0; + + endpoint_t *endpoint = window_covering_device::create(node::get(), &window_covering_config, ENDPOINT_FLAG_NONE, (void *)this); + if (endpoint == nullptr) { + log_e("Failed to create window covering endpoint"); + return false; + } + + setEndPointId(endpoint::get_id(endpoint)); + log_i("Window Covering created with endpoint_id %d", getEndPointId()); + + // Get the Window Covering cluster and add features and commands + cluster_t *window_covering_cluster = cluster::get(endpoint, WindowCovering::Id); + if (window_covering_cluster != nullptr) { + // Add Lift feature + feature::lift::config_t lift_config; + lift_config.number_of_actuations_lift = 0; + if (feature::lift::add(window_covering_cluster, &lift_config) != ESP_OK) { + log_e("Failed to add Lift feature"); + } + + // Add Position Aware Lift feature + feature::position_aware_lift::config_t position_aware_lift_config; + position_aware_lift_config.current_position_lift_percentage = nullable(0); + position_aware_lift_config.target_position_lift_percent_100ths = nullable(liftPercent * 100); + position_aware_lift_config.current_position_lift_percent_100ths = nullable(liftPercent * 100); + if (feature::position_aware_lift::add(window_covering_cluster, &position_aware_lift_config) != ESP_OK) { + log_e("Failed to add Position Aware Lift feature"); + } + + // Add Tilt feature if the covering type supports it + bool supportsTilt = (coveringType == SHUTTER || coveringType == BLIND_TILT_ONLY || coveringType == BLIND_LIFT_AND_TILT); + if (supportsTilt) { + feature::tilt::config_t tilt_config; + tilt_config.number_of_actuations_tilt = 0; + if (feature::tilt::add(window_covering_cluster, &tilt_config) != ESP_OK) { + log_e("Failed to add Tilt feature"); + } + + // Add Position Aware Tilt feature + feature::position_aware_tilt::config_t position_aware_tilt_config; + position_aware_tilt_config.current_position_tilt_percentage = nullable(0); + position_aware_tilt_config.target_position_tilt_percent_100ths = nullable(tiltPercent * 100); + position_aware_tilt_config.current_position_tilt_percent_100ths = nullable(tiltPercent * 100); + if (feature::position_aware_tilt::add(window_covering_cluster, &position_aware_tilt_config) != ESP_OK) { + log_e("Failed to add Position Aware Tilt feature"); + } + } + + // Add Absolute Position feature (creates InstalledOpenLimitLift/ClosedLimitLift/Tilt attributes) + // Must be added AFTER all lift and tilt features for all attributes to be created + feature::absolute_position::config_t absolute_position_config; + absolute_position_config.installed_open_limit_lift = 0; + absolute_position_config.installed_closed_limit_lift = 65534; + absolute_position_config.installed_open_limit_tilt = 0; + absolute_position_config.installed_closed_limit_tilt = 65534; + if (feature::absolute_position::add(window_covering_cluster, &absolute_position_config) != ESP_OK) { + log_e("Failed to add Absolute Position feature"); + } + + // Create Window Covering commands + create_up_or_open(window_covering_cluster); + create_down_or_close(window_covering_cluster); + create_stop_motion(window_covering_cluster); + create_go_to_lift_value(window_covering_cluster); + create_go_to_lift_percentage(window_covering_cluster); + if (supportsTilt) { + create_go_to_tilt_value(window_covering_cluster); + create_go_to_tilt_percentage(window_covering_cluster); + } + } else { + log_e("Failed to get Window Covering cluster for feature and command creation"); + } + + started = true; + + // Set initial lift and tilt percentages + if (liftPercent > 0) { + setLiftPercentage(liftPercent); + } + if (tiltPercent > 0) { + setTiltPercentage(tiltPercent); + } + + return true; +} + +void MatterWindowCovering::end() { + started = false; +} + +bool MatterWindowCovering::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 Window Covering device has not begun."); + return false; + } + + log_d("Window Covering Attr update callback: endpoint: %u, cluster: %u, attribute: %u", endpoint_id, cluster_id, attribute_id); + + if (endpoint_id == getEndPointId() && cluster_id == WindowCovering::Id) { + switch (attribute_id) { + // Current position attributes (read-only to external Matter controllers; updated internally by device) + case WindowCovering::Attributes::CurrentPositionLiftPercent100ths::Id: + { + uint16_t liftPercent100ths = val->val.u16; + uint8_t liftPercent = (uint8_t)(liftPercent100ths / 100); + log_d("Window Covering Lift Percentage changed to %d%%", liftPercent); + if (currentLiftPercent != liftPercent) { + if (_onChangeCB != NULL) { + ret &= _onChangeCB(liftPercent, currentTiltPercent); + } + if (ret == true) { + currentLiftPercent = liftPercent; + } + } + break; + } + case WindowCovering::Attributes::CurrentPositionTiltPercent100ths::Id: + { + uint16_t tiltPercent100ths = val->val.u16; + uint8_t tiltPercent = (uint8_t)(tiltPercent100ths / 100); + log_d("Window Covering Tilt Percentage changed to %d%%", tiltPercent); + if (currentTiltPercent != tiltPercent) { + if (_onChangeCB != NULL) { + ret &= _onChangeCB(currentLiftPercent, tiltPercent); + } + if (ret == true) { + currentTiltPercent = tiltPercent; + } + } + break; + } + case WindowCovering::Attributes::CurrentPositionLift::Id: + log_d("Window Covering Lift Position changed to %d", val->val.u16); + currentLiftPosition = val->val.u16; + break; + case WindowCovering::Attributes::CurrentPositionTilt::Id: + log_d("Window Covering Tilt Position changed to %d", val->val.u16); + currentTiltPosition = val->val.u16; + break; + case WindowCovering::Attributes::CurrentPositionLiftPercentage::Id: log_d("Window Covering Lift Percentage (legacy) changed to %d%%", val->val.u8); break; + case WindowCovering::Attributes::CurrentPositionTiltPercentage::Id: log_d("Window Covering Tilt Percentage (legacy) changed to %d%%", val->val.u8); break; + + // Target position attributes (writable, trigger movement) + // Note: TargetPosition is where the device SHOULD go, not where it is. + // CurrentPosition should only be updated when the physical device actually moves. + case WindowCovering::Attributes::TargetPositionLiftPercent100ths::Id: + { + if (!chip::app::NumericAttributeTraits::IsNullValue(val->val.u16)) { + uint16_t targetLiftPercent100ths = val->val.u16; + uint8_t targetLiftPercent = (uint8_t)(targetLiftPercent100ths / 100); + log_d("Window Covering Target Lift Percentage changed to %d%%", targetLiftPercent); + // Call callback to trigger movement - do NOT update currentLiftPercent here + // `CurrentPosition` will be updated by the application when the device actually moves + // Get current position to detect StopMotion command + uint16_t currentLiftPercent100ths = 0; + esp_matter_attr_val_t currentVal = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLiftPercent100ths::Id, ¤tVal)) { + if (!chip::app::NumericAttributeTraits::IsNullValue(currentVal.val.u16)) { + currentLiftPercent100ths = currentVal.val.u16; + log_d("Window Covering Current Lift Percentage is %d%%", (uint8_t)(currentLiftPercent100ths / 100)); + } + } + + // Detect command type based on target value and call appropriate callbacks + // Commands modify TargetPositionLiftPercent100ths: + // - UpOrOpen: sets TargetPosition = 0 (WC_PERCENT100THS_MIN_OPEN) + // - DownOrClose: sets TargetPosition = 10000 (WC_PERCENT100THS_MAX_CLOSED) + // - StopMotion: sets TargetPosition = CurrentPosition + // Priority: UpOrOpen/DownOrClose > StopMotion > GoToLiftPercentage + // Note: If StopMotion is executed when CurrentPosition is at 0 or 10000, + // it will be detected as UpOrOpen/DownOrClose (acceptable behavior) + if (targetLiftPercent100ths == 0) { + // UpOrOpen command - fully open (priority check) + log_d("Window Covering: UpOrOpen command detected"); + if (_onOpenCB != NULL) { + ret &= _onOpenCB(); + } + } else if (targetLiftPercent100ths == 10000) { + // DownOrClose command - fully closed (priority check) + log_d("Window Covering: DownOrClose command detected"); + if (_onCloseCB != NULL) { + ret &= _onCloseCB(); + } + } else if (targetLiftPercent100ths == currentLiftPercent100ths && currentLiftPercent100ths != 0 && currentLiftPercent100ths != 10000) { + // StopMotion command - target equals current position (but not at limits) + // This detects StopMotion when TargetPosition is set to CurrentPosition + // and CurrentPosition is not at the limits (0 or 10000) + log_d("Window Covering: StopMotion command detected"); + if (_onStopCB != NULL) { + ret &= _onStopCB(); + } + } + + // Always call the generic onGoToLiftPercentage callback for compatibility + // This handles all target position changes, including commands and direct attribute writes + if (_onGoToLiftPercentageCB != NULL) { + ret &= _onGoToLiftPercentageCB(targetLiftPercent); + } + } else { + log_d("Window Covering Target Lift Percentage set to NULL"); + } + break; + } + case WindowCovering::Attributes::TargetPositionTiltPercent100ths::Id: + { + if (!chip::app::NumericAttributeTraits::IsNullValue(val->val.u16)) { + uint16_t targetTiltPercent100ths = val->val.u16; + uint8_t targetTiltPercent = (uint8_t)(targetTiltPercent100ths / 100); + log_d("Window Covering Target Tilt Percentage changed to %d%%", targetTiltPercent); + // Call callback to trigger movement - do NOT update currentTiltPercent here + // CurrentPosition will be updated by the application when the device actually moves + if (_onGoToTiltPercentageCB != NULL) { + ret &= _onGoToTiltPercentageCB(targetTiltPercent); + } + } else { + log_d("Window Covering Target Tilt Percentage set to NULL"); + } + break; + } + + // Configuration attributes + case WindowCovering::Attributes::Type::Id: + log_d("Window Covering Type changed to %d", val->val.u8); + coveringType = (WindowCoveringType_t)val->val.u8; + break; + case WindowCovering::Attributes::EndProductType::Id: log_d("Window Covering End Product Type changed to %d", val->val.u8); break; + case WindowCovering::Attributes::ConfigStatus::Id: log_d("Window Covering Config Status changed to 0x%02X", val->val.u8); break; + case WindowCovering::Attributes::Mode::Id: log_d("Window Covering Mode changed to 0x%02X", val->val.u8); break; + + // Operational status attributes + case WindowCovering::Attributes::OperationalStatus::Id: log_d("Window Covering Operational Status changed to 0x%02X", val->val.u8); break; + case WindowCovering::Attributes::SafetyStatus::Id: log_d("Window Covering Safety Status changed to 0x%04X", val->val.u16); break; + + // Limit attributes + case WindowCovering::Attributes::PhysicalClosedLimitLift::Id: log_d("Window Covering Physical Closed Limit Lift changed to %d", val->val.u16); break; + case WindowCovering::Attributes::PhysicalClosedLimitTilt::Id: log_d("Window Covering Physical Closed Limit Tilt changed to %d", val->val.u16); break; + case WindowCovering::Attributes::InstalledOpenLimitLift::Id: log_d("Window Covering Installed Open Limit Lift changed to %d", val->val.u16); break; + case WindowCovering::Attributes::InstalledClosedLimitLift::Id: log_d("Window Covering Installed Closed Limit Lift changed to %d", val->val.u16); break; + case WindowCovering::Attributes::InstalledOpenLimitTilt::Id: log_d("Window Covering Installed Open Limit Tilt changed to %d", val->val.u16); break; + case WindowCovering::Attributes::InstalledClosedLimitTilt::Id: log_d("Window Covering Installed Closed Limit Tilt changed to %d", val->val.u16); break; + + // Actuation count attributes + case WindowCovering::Attributes::NumberOfActuationsLift::Id: log_d("Window Covering Number of Actuations Lift changed to %d", val->val.u16); break; + case WindowCovering::Attributes::NumberOfActuationsTilt::Id: log_d("Window Covering Number of Actuations Tilt changed to %d", val->val.u16); break; + + default: log_d("Window Covering Unknown attribute %u changed", attribute_id); break; + } + } + return ret; +} + +bool MatterWindowCovering::setLiftPosition(uint16_t liftPosition) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (currentLiftPosition == liftPosition) { + return true; + } + + // Get InstalledOpenLimitLift and InstalledClosedLimitLift for conversion + uint16_t openLimit = 0; + uint16_t closedLimit = 0; + esp_matter_attr_val_t limitVal = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitLift::Id, &limitVal)) { + openLimit = limitVal.val.u16; + } + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitLift::Id, &limitVal)) { + closedLimit = limitVal.val.u16; + } + + // Convert absolute position to percent100ths + // Using the same logic as ESP-Matter's LiftToPercent100ths + uint16_t liftPercent100ths = 0; + if (openLimit != closedLimit) { + // Linear interpolation between open (0% = 0) and closed (100% = 10000) + if (openLimit < closedLimit) { + // Normal: open < closed + if (liftPosition <= openLimit) { + liftPercent100ths = 0; // Fully open + } else if (liftPosition >= closedLimit) { + liftPercent100ths = 10000; // Fully closed + } else { + liftPercent100ths = ((liftPosition - openLimit) * 10000) / (closedLimit - openLimit); + } + } else { + // Inverted: open > closed + if (liftPosition >= openLimit) { + liftPercent100ths = 0; // Fully open + } else if (liftPosition <= closedLimit) { + liftPercent100ths = 10000; // Fully closed + } else { + liftPercent100ths = ((openLimit - liftPosition) * 10000) / (openLimit - closedLimit); + } + } + } + + // Update CurrentPositionLift (absolute) + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLift::Id, &val)) { + log_e("Failed to get Lift Position Attribute."); + return false; + } + + if (val.val.u16 != liftPosition) { + val.val.u16 = liftPosition; + bool ret = updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLift::Id, &val); + if (ret) { + currentLiftPosition = liftPosition; + // Also update CurrentPositionLiftPercent100ths to keep attributes in sync + // This matches ESP-Matter's LiftPositionSet() behavior + setLiftPercentage((uint8_t)(liftPercent100ths / 100)); + } + return ret; + } + return true; +} + +uint16_t MatterWindowCovering::getLiftPosition() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLift::Id, &val)) { + currentLiftPosition = val.val.u16; + } + return currentLiftPosition; +} + +bool MatterWindowCovering::setLiftPercentage(uint8_t liftPercent) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (liftPercent > 100) { + log_e("Lift percentage must be between 0 and 100"); + return false; + } + + if (currentLiftPercent == liftPercent) { + return true; + } + + // Matter uses percent100ths (0-10000 for 0-100%) + uint16_t liftPercent100ths = liftPercent * 100; + + // Update only CurrentPosition, not TargetPosition + // TargetPosition is set by Matter commands/apps, CurrentPosition reflects actual position + esp_matter_attr_val_t currentVal = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLiftPercent100ths::Id, ¤tVal)) { + log_e("Failed to get Current Lift Percentage Attribute."); + return false; + } + + if (currentVal.val.u16 != liftPercent100ths) { + currentVal.val.u16 = liftPercent100ths; + bool ret = updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLiftPercent100ths::Id, ¤tVal); + if (ret) { + currentLiftPercent = liftPercent; + } + return ret; + } + return true; +} + +uint8_t MatterWindowCovering::getLiftPercentage() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionLiftPercent100ths::Id, &val)) { + currentLiftPercent = (uint8_t)(val.val.u16 / 100); + } + return currentLiftPercent; +} + +bool MatterWindowCovering::setTargetLiftPercent100ths(uint16_t liftPercent100ths) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (liftPercent100ths > 10000) { + log_e("Lift percent100ths must be between 0 and 10000"); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionLiftPercent100ths::Id, &val)) { + log_e("Failed to get Target Lift Percentage Attribute."); + return false; + } + + if (val.val.u16 != liftPercent100ths) { + val.val.u16 = liftPercent100ths; + return updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionLiftPercent100ths::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getTargetLiftPercent100ths() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionLiftPercent100ths::Id, &val)) { + if (!chip::app::NumericAttributeTraits::IsNullValue(val.val.u16)) { + return val.val.u16; + } + } + return 0; +} + +bool MatterWindowCovering::setTiltPosition(uint16_t tiltPosition) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (currentTiltPosition == tiltPosition) { + return true; + } + + // Get InstalledOpenLimitTilt and InstalledClosedLimitTilt for conversion + uint16_t openLimit = 0; + uint16_t closedLimit = 0; + esp_matter_attr_val_t limitVal = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitTilt::Id, &limitVal)) { + openLimit = limitVal.val.u16; + } + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitTilt::Id, &limitVal)) { + closedLimit = limitVal.val.u16; + } + + // Convert absolute position to percent100ths + // Using the same logic as ESP-Matter's TiltToPercent100ths + uint16_t tiltPercent100ths = 0; + if (openLimit != closedLimit) { + // Linear interpolation between open (0% = 0) and closed (100% = 10000) + if (openLimit < closedLimit) { + // Normal: open < closed + if (tiltPosition <= openLimit) { + tiltPercent100ths = 0; // Fully open + } else if (tiltPosition >= closedLimit) { + tiltPercent100ths = 10000; // Fully closed + } else { + tiltPercent100ths = ((tiltPosition - openLimit) * 10000) / (closedLimit - openLimit); + } + } else { + // Inverted: open > closed + if (tiltPosition >= openLimit) { + tiltPercent100ths = 0; // Fully open + } else if (tiltPosition <= closedLimit) { + tiltPercent100ths = 10000; // Fully closed + } else { + tiltPercent100ths = ((openLimit - tiltPosition) * 10000) / (openLimit - closedLimit); + } + } + } + + // Update CurrentPositionTilt (absolute) + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTilt::Id, &val)) { + log_e("Failed to get Tilt Position Attribute."); + return false; + } + + if (val.val.u16 != tiltPosition) { + val.val.u16 = tiltPosition; + bool ret = updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTilt::Id, &val); + if (ret) { + currentTiltPosition = tiltPosition; + // Also update CurrentPositionTiltPercent100ths to keep attributes in sync + // This matches ESP-Matter's TiltPositionSet() behavior + setTiltPercentage((uint8_t)(tiltPercent100ths / 100)); + } + return ret; + } + return true; +} + +uint16_t MatterWindowCovering::getTiltPosition() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTilt::Id, &val)) { + currentTiltPosition = val.val.u16; + } + return currentTiltPosition; +} + +bool MatterWindowCovering::setTiltPercentage(uint8_t tiltPercent) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (tiltPercent > 100) { + log_e("Tilt percentage must be between 0 and 100"); + return false; + } + + if (currentTiltPercent == tiltPercent) { + return true; + } + + // Matter uses percent100ths (0-10000 for 0-100%) + uint16_t tiltPercent100ths = tiltPercent * 100; + + // Update only CurrentPosition, not TargetPosition + // TargetPosition is set by Matter commands/apps, CurrentPosition reflects actual position + esp_matter_attr_val_t currentVal = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTiltPercent100ths::Id, ¤tVal)) { + log_e("Failed to get Current Tilt Percentage Attribute."); + return false; + } + + if (currentVal.val.u16 != tiltPercent100ths) { + currentVal.val.u16 = tiltPercent100ths; + bool ret = updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTiltPercent100ths::Id, ¤tVal); + if (ret) { + currentTiltPercent = tiltPercent; + } + return ret; + } + return true; +} + +uint8_t MatterWindowCovering::getTiltPercentage() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::CurrentPositionTiltPercent100ths::Id, &val)) { + currentTiltPercent = (uint8_t)(val.val.u16 / 100); + } + return currentTiltPercent; +} + +bool MatterWindowCovering::setTargetTiltPercent100ths(uint16_t tiltPercent100ths) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + if (tiltPercent100ths > 10000) { + log_e("Tilt percent100ths must be between 0 and 10000"); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionTiltPercent100ths::Id, &val)) { + log_e("Failed to get Target Tilt Percentage Attribute."); + return false; + } + + if (val.val.u16 != tiltPercent100ths) { + val.val.u16 = tiltPercent100ths; + return updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionTiltPercent100ths::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getTargetTiltPercent100ths() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::TargetPositionTiltPercent100ths::Id, &val)) { + if (!chip::app::NumericAttributeTraits::IsNullValue(val.val.u16)) { + return val.val.u16; + } + } + return 0; +} + +bool MatterWindowCovering::setInstalledOpenLimitLift(uint16_t openLimit) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitLift::Id, &val)) { + log_e("Failed to get Installed Open Limit Lift Attribute"); + return false; + } + + if (val.val.u16 != openLimit) { + val.val.u16 = openLimit; + return setAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitLift::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getInstalledOpenLimitLift() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitLift::Id, &val)) { + return val.val.u16; + } + return 0; +} + +bool MatterWindowCovering::setInstalledClosedLimitLift(uint16_t closedLimit) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitLift::Id, &val)) { + log_e("Failed to get Installed Closed Limit Lift Attribute."); + return false; + } + + if (val.val.u16 != closedLimit) { + val.val.u16 = closedLimit; + return setAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitLift::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getInstalledClosedLimitLift() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitLift::Id, &val)) { + return val.val.u16; + } + return 0; +} + +bool MatterWindowCovering::setInstalledOpenLimitTilt(uint16_t openLimit) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitTilt::Id, &val)) { + log_e("Failed to get Installed Open Limit Tilt Attribute."); + return false; + } + + if (val.val.u16 != openLimit) { + val.val.u16 = openLimit; + return setAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitTilt::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getInstalledOpenLimitTilt() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledOpenLimitTilt::Id, &val)) { + return val.val.u16; + } + return 0; +} + +bool MatterWindowCovering::setInstalledClosedLimitTilt(uint16_t closedLimit) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitTilt::Id, &val)) { + log_e("Failed to get Installed Closed Limit Tilt Attribute."); + return false; + } + + if (val.val.u16 != closedLimit) { + val.val.u16 = closedLimit; + return setAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitTilt::Id, &val); + } + return true; +} + +uint16_t MatterWindowCovering::getInstalledClosedLimitTilt() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::InstalledClosedLimitTilt::Id, &val)) { + return val.val.u16; + } + return 0; +} + +bool MatterWindowCovering::setCoveringType(WindowCoveringType_t coveringType) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::Type::Id, &val)) { + log_e("Failed to get Window Covering Type Attribute."); + return false; + } + + if (val.val.u8 != (uint8_t)coveringType) { + val.val.u8 = (uint8_t)coveringType; + bool ret = updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::Type::Id, &val); + if (ret) { + this->coveringType = coveringType; + } + return ret; + } + return true; +} + +MatterWindowCovering::WindowCoveringType_t MatterWindowCovering::getCoveringType() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::Type::Id, &val)) { + coveringType = (WindowCoveringType_t)val.val.u8; + } + return coveringType; +} + +bool MatterWindowCovering::setOperationalStatus(uint8_t operationalStatus) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val)) { + log_e("Failed to get Operational Status Attribute."); + return false; + } + + if (val.val.u8 != operationalStatus) { + val.val.u8 = operationalStatus; + return updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val); + } + return true; +} + +uint8_t MatterWindowCovering::getOperationalStatus() { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val)) { + return val.val.u8; + } + return 0; +} + +bool MatterWindowCovering::setOperationalState(OperationalStatusField_t field, OperationalState_t state) { + if (!started) { + log_e("Matter Window Covering device has not begun."); + return false; + } + + // ESP-Matter only allows setting Lift or Tilt, not Global directly + // Global is automatically updated based on Lift (priority) or Tilt + if (field == GLOBAL) { + log_e("Cannot set Global operational state directly. Set Lift or Tilt instead."); + return false; + } + + if (field != LIFT && field != TILT) { + log_e("Invalid Operational Status Field. Only LIFT or TILT are allowed."); + return false; + } + + // Get current operational status + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val)) { + log_e("Failed to get Operational Status Attribute."); + return false; + } + + uint8_t currentStatus = val.val.u8; + uint8_t fieldMask = (uint8_t)field; + // For clarity: LIFT uses shift 2 (bits 2-3), TILT uses shift 4 (bits 4-5) + uint8_t fieldShift = (field == LIFT) ? 2 : 4; + + // Extract current state for this field + uint8_t currentFieldState = (currentStatus & fieldMask) >> fieldShift; + + // Only update if state changed + if (currentFieldState != (uint8_t)state) { + // Clear the field and set new state + currentStatus = (currentStatus & ~fieldMask) | (((uint8_t)state << fieldShift) & fieldMask); + + // Following ESP-Matter behavior: + // 1. Set the field (Lift or Tilt) to the new state + // 2. Temporarily set Global to the same state + // 3. Recalculate Global based on priority: Lift (if not Stall) > Tilt + uint8_t liftState = (currentStatus & LIFT) >> 2; + uint8_t tiltState = (currentStatus & TILT) >> 4; + + // Global follows Lift by priority, or Tilt if Lift is not active (Stall = 0) + uint8_t globalState = (liftState != STALL) ? liftState : tiltState; + + // Update Global field (bits 0-1) + currentStatus = (currentStatus & ~GLOBAL) | (globalState << 0); + + val.val.u8 = currentStatus; + return updateAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val); + } + return true; +} + +MatterWindowCovering::OperationalState_t MatterWindowCovering::getOperationalState(OperationalStatusField_t field) { + esp_matter_attr_val_t val = esp_matter_invalid(NULL); + if (!getAttributeVal(WindowCovering::Id, WindowCovering::Attributes::OperationalStatus::Id, &val)) { + return STALL; + } + + uint8_t operationalStatus = val.val.u8; + uint8_t fieldMask = (uint8_t)field; + uint8_t fieldShift = 0; + + // Determine shift based on field + if (field == GLOBAL) { + fieldShift = 0; // Bits 0-1 + } else if (field == LIFT) { + fieldShift = 2; // Bits 2-3 + } else if (field == TILT) { + fieldShift = 4; // Bits 4-5 + } else { + return STALL; + } + + // Extract state for this field + uint8_t fieldState = (operationalStatus & fieldMask) >> fieldShift; + return (OperationalState_t)fieldState; +} + +void MatterWindowCovering::updateAccessory() { + if (_onChangeCB != NULL) { + _onChangeCB(currentLiftPercent, currentTiltPercent); + } +} + +#endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Matter/src/MatterEndpoints/MatterWindowCovering.h b/libraries/Matter/src/MatterEndpoints/MatterWindowCovering.h new file mode 100644 index 00000000000..740729df43a --- /dev/null +++ b/libraries/Matter/src/MatterEndpoints/MatterWindowCovering.h @@ -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. + +#pragma once +#include +#ifdef CONFIG_ESP_MATTER_ENABLE_DATA_MODEL + +#include +#include +#include +#include + +using namespace chip::app::Clusters; + +// Matter Window Covering endpoint with Lift and Tilt control + +class MatterWindowCovering : public MatterEndPoint { +public: + // Window Covering Type constants (from Matter spec) + enum WindowCoveringType_t { + ROLLERSHADE = (uint8_t)WindowCovering::Type::kRollerShade, // Roller Shade (LIFT support) + ROLLERSHADE_2_MOTOR = (uint8_t)WindowCovering::Type::kRollerShade2Motor, // Roller Shade 2 Motor (LIFT support) + ROLLERSHADE_EXTERIOR = (uint8_t)WindowCovering::Type::kRollerShadeExterior, // Roller Shade Exterior (LIFT support) + ROLLERSHADE_EXTERIOR_2_MOTOR = (uint8_t)WindowCovering::Type::kRollerShadeExterior2Motor, // Roller Shade Exterior 2 Motor (LIFT support) + DRAPERY = (uint8_t)WindowCovering::Type::kDrapery, // Drapery (LIFT support) + AWNING = (uint8_t)WindowCovering::Type::kAwning, // Awning (LIFT support) + SHUTTER = (uint8_t)WindowCovering::Type::kShutter, // Shutter (TILT support) + BLIND_TILT_ONLY = (uint8_t)WindowCovering::Type::kTiltBlindTiltOnly, // Blind Tilt Only (TILT support) + BLIND_LIFT_AND_TILT = (uint8_t)WindowCovering::Type::kTiltBlindLiftAndTilt, // Blind Lift and Tilt (LIFT and TILT support) + PROJECTOR_SCREEN = (uint8_t)WindowCovering::Type::kProjectorScreen, // Projector Screen (LIFT support) + }; + + // Operational State constants (from Matter spec) + enum OperationalState_t { + STALL = (uint8_t)WindowCovering::OperationalState::Stall, // Currently not moving + MOVING_UP_OR_OPEN = (uint8_t)WindowCovering::OperationalState::MovingUpOrOpen, // Is currently opening + MOVING_DOWN_OR_CLOSE = (uint8_t)WindowCovering::OperationalState::MovingDownOrClose, // Is currently closing + }; + + // Operational Status field constants (from Matter spec) + enum OperationalStatusField_t { + GLOBAL = (uint8_t)WindowCovering::OperationalStatus::kGlobal, // Global operational state field 0x03 (bits 0-1) + LIFT = (uint8_t)WindowCovering::OperationalStatus::kLift, // Lift operational state field 0x0C (bits 2-3) + TILT = (uint8_t)WindowCovering::OperationalStatus::kTilt, // Tilt operational state field 0x30 (bits 4-5) + }; + + MatterWindowCovering(); + ~MatterWindowCovering(); + virtual bool begin(uint8_t liftPercent = 100, uint8_t tiltPercent = 0, WindowCoveringType_t coveringType = ROLLERSHADE); + void end(); // this will just stop processing Matter events. + + // Lift position control + bool setLiftPosition(uint16_t liftPosition); + uint16_t getLiftPosition(); + bool setLiftPercentage(uint8_t liftPercent); + uint8_t getLiftPercentage(); + bool setTargetLiftPercent100ths(uint16_t liftPercent100ths); + uint16_t getTargetLiftPercent100ths(); + + // Lift limit control + bool setInstalledOpenLimitLift(uint16_t openLimit); + uint16_t getInstalledOpenLimitLift(); + bool setInstalledClosedLimitLift(uint16_t closedLimit); + uint16_t getInstalledClosedLimitLift(); + + // Tilt position control + bool setTiltPosition(uint16_t tiltPosition); + uint16_t getTiltPosition(); + bool setTiltPercentage(uint8_t tiltPercent); + uint8_t getTiltPercentage(); + bool setTargetTiltPercent100ths(uint16_t tiltPercent100ths); + uint16_t getTargetTiltPercent100ths(); + + // Tilt limit control + bool setInstalledOpenLimitTilt(uint16_t openLimit); + uint16_t getInstalledOpenLimitTilt(); + bool setInstalledClosedLimitTilt(uint16_t closedLimit); + uint16_t getInstalledClosedLimitTilt(); + + // Window covering type + bool setCoveringType(WindowCoveringType_t coveringType); + WindowCoveringType_t getCoveringType(); + + // Operational status control (full bitmap) + bool setOperationalStatus(uint8_t operationalStatus); + uint8_t getOperationalStatus(); + + // Operational state control (individual fields) + bool setOperationalState(OperationalStatusField_t field, OperationalState_t state); + OperationalState_t getOperationalState(OperationalStatusField_t field); + + // User Callback for whenever the window covering is opened + using EndPointOpenCB = std::function; + void onOpen(EndPointOpenCB onChangeCB) { + _onOpenCB = onChangeCB; + } + + // User Callback for whenever the window covering is closed + using EndPointCloseCB = std::function; + void onClose(EndPointCloseCB onChangeCB) { + _onCloseCB = onChangeCB; + } + + // User Callback for whenever the lift percentage is changed + using EndPointLiftCB = std::function; + void onGoToLiftPercentage(EndPointLiftCB onChangeCB) { + _onGoToLiftPercentageCB = onChangeCB; + } + + // User Callback for whenever the tilt percentage is changed + using EndPointTiltCB = std::function; + void onGoToTiltPercentage(EndPointTiltCB onChangeCB) { + _onGoToTiltPercentageCB = onChangeCB; + } + + // User Callback for whenever the window covering is stopped + using EndPointStopCB = std::function; + void onStop(EndPointStopCB onChangeCB) { + _onStopCB = onChangeCB; + } + + // User Callback for whenever any parameter is changed + using EndPointCB = std::function; + void onChange(EndPointCB onChangeCB) { + _onChangeCB = onChangeCB; + } + + // used to update the state of the window covering using the current Matter internal state + // It is necessary to set a user callback function using onChange() to handle the physical window covering state + void updateAccessory(); + + // 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; + uint8_t currentLiftPercent = 0; + uint16_t currentLiftPosition = 0; + uint8_t currentTiltPercent = 0; + uint16_t currentTiltPosition = 0; + WindowCoveringType_t coveringType = ROLLERSHADE; + + EndPointOpenCB _onOpenCB = NULL; + EndPointCloseCB _onCloseCB = NULL; + EndPointLiftCB _onGoToLiftPercentageCB = NULL; + EndPointTiltCB _onGoToTiltPercentageCB = NULL; + EndPointStopCB _onStopCB = NULL; + EndPointCB _onChangeCB = NULL; +}; +#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 7a28a36e016..527a4aa1835 100644 --- a/libraries/NetBIOS/library.properties +++ b/libraries/NetBIOS/library.properties @@ -1,5 +1,5 @@ name=NetBIOS -version=3.3.2 +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 5c848a0fcc1..f6c465eebb5 100644 --- a/libraries/Network/library.properties +++ b/libraries/Network/library.properties @@ -1,5 +1,5 @@ name=Networking -version=3.3.2 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=General network management library. 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 dafac9cd983..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(); @@ -31,6 +34,4 @@ class NetworkManager : public NetworkEvents, public Printable { } }; -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETWORK) extern NetworkManager Network; -#endif 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 8d74c2b8f37..2b867985222 100644 --- a/libraries/NetworkClientSecure/library.properties +++ b/libraries/NetworkClientSecure/library.properties @@ -1,5 +1,5 @@ name=NetworkClientSecure -version=3.3.2 +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/ssl_client.cpp b/libraries/NetworkClientSecure/src/ssl_client.cpp index f70aefec034..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); 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 147793e59d6..68b9c85f864 100644 --- a/libraries/OpenThread/library.properties +++ b/libraries/OpenThread/library.properties @@ -1,5 +1,5 @@ name=OpenThread -version=3.3.2 +version=3.3.4 author=Rodrigo Garcia | GitHub @SuGlider maintainer=Rodrigo Garcia sentence=Library for OpenThread Network on ESP32. 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 eaaf349d847..b6ce0d16f34 100644 --- a/libraries/PPP/library.properties +++ b/libraries/PPP/library.properties @@ -1,5 +1,5 @@ name=PPP -version=3.3.2 +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 189825b61a1..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(); diff --git a/libraries/Preferences/library.properties b/libraries/Preferences/library.properties index aa64864c7bd..a9e3932888e 100644 --- a/libraries/Preferences/library.properties +++ b/libraries/Preferences/library.properties @@ -1,5 +1,5 @@ name=Preferences -version=3.3.2 +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 580c9115cfb..af0c63b233b 100644 --- a/libraries/RainMaker/library.properties +++ b/libraries/RainMaker/library.properties @@ -1,5 +1,5 @@ name=ESP RainMaker -version=3.3.2 +version=3.3.4 author=Sweety Mhaiske maintainer=Hristo Gochkov sentence=ESP RainMaker Support 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 2c5a2b6a16c..4bb9876e684 100644 --- a/libraries/SD/library.properties +++ b/libraries/SD/library.properties @@ -1,5 +1,5 @@ name=SD -version=3.3.2 +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_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/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 f7fe194e586..6011ed115ea 100644 --- a/libraries/SD_MMC/library.properties +++ b/libraries/SD_MMC/library.properties @@ -1,5 +1,5 @@ name=SD_MMC -version=3.3.2 +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/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 1e9b1aa5a5b..15a17d9101c 100644 --- a/libraries/SPI/library.properties +++ b/libraries/SPI/library.properties @@ -1,5 +1,5 @@ name=SPI -version=3.3.2 +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/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 f516aed2ee2..923172dd6ae 100644 --- a/libraries/SPIFFS/library.properties +++ b/libraries/SPIFFS/library.properties @@ -1,5 +1,5 @@ name=SPIFFS -version=3.3.2 +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/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 b6f189bb7f0..cfa030f860d 100644 --- a/libraries/SimpleBLE/library.properties +++ b/libraries/SimpleBLE/library.properties @@ -1,5 +1,5 @@ name=SimpleBLE -version=3.3.2 +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/TFLiteMicro/library.properties b/libraries/TFLiteMicro/library.properties index 8e5f6d95718..52641b149d1 100644 --- a/libraries/TFLiteMicro/library.properties +++ b/libraries/TFLiteMicro/library.properties @@ -1,5 +1,5 @@ name=TFLite Micro -version=3.3.2 +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 b978f9a805e..441519edab0 100644 --- a/libraries/Ticker/library.properties +++ b/libraries/Ticker/library.properties @@ -1,5 +1,5 @@ name=Ticker -version=3.3.2 +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/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/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 d9229c9d7b9..8698f2cffbe 100644 --- a/libraries/USB/library.properties +++ b/libraries/USB/library.properties @@ -1,5 +1,5 @@ name=USB -version=3.3.2 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32S2 USB Library diff --git a/libraries/Update/README.md b/libraries/Update/README.md new file mode 100644 index 00000000000..7483cb35308 --- /dev/null +++ b/libraries/Update/README.md @@ -0,0 +1,431 @@ +# ESP32 Arduino Update Library + +The Update library provides functionality for Over-The-Air (OTA) firmware updates on ESP32 devices. It supports secure updates with signature verification, encrypted updates, and various update sources. + +## Features + +- **OTA Updates**: Update firmware over Wi-Fi +- **Signature Verification**: RSA and ECDSA signature verification for secure updates (optional, must be enabled with `UPDATE_SIGN`) +- **Image Encryption**: Support for encrypted firmware updates (optional, can be disabled with `UPDATE_NOCRYPT`) +- **Multiple Sources**: HTTP, HTTPS, SD card, and custom sources +- **Progress Callbacks**: Monitor update progress +- **MD5 Verification**: Optional MD5 checksum verification + +## Quick Start + +### Basic OTA Update + +```cpp +#include + +WiFiClient client; +size_t updateSize = client.available(); + +if (Update.begin(updateSize)) { + Update.writeStream(client); + if (Update.end()) { + Serial.println("Update successful!"); + ESP.restart(); + } else { + Serial.println("Update failed!"); + } +} +``` + +### Signed OTA Update (Recommended) + +To enable signature verification, add `-DUPDATE_SIGN` to your build flags (e.g., in `build_opt.h`): +``` +-DUPDATE_SIGN +``` + +Then in your sketch: +```cpp +#include + +// Include your public key (generated with bin_signing.py) +#include "public_key.h" + +// Create verifier object (defaults to SHA-256) +UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN); + +// Install signature verification BEFORE Update.begin() +Update.installSignature(&sign); + +// Now perform the update as usual +if (Update.begin(updateSize)) { + Update.writeStream(client); + if (Update.end()) { + // Signature was verified successfully! + Serial.println("Signed update successful!"); + ESP.restart(); + } else { + if (Update.getError() == UPDATE_ERROR_SIGN) { + Serial.println("Signature verification failed!"); + } + } +} +``` + +## Signature Verification + +### Overview + +Code signing ensures that only firmware signed with your private key will be accepted by your devices. This protects against: + +- Unauthorized firmware updates +- Man-in-the-middle attacks +- Compromised update servers +- Supply chain attacks + +### Supported Algorithms + +**Signature Schemes:** +- RSA-2048, RSA-3072, RSA-4096 +- ECDSA-P256, ECDSA-P384 + +**Hash Algorithms:** +- SHA-256, SHA-384, SHA-512 + +### Setup + +1. **Generate Key Pair:** + +```bash +# RSA-2048 (recommended) +python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem +python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + +# ECDSA-P256 (smaller, faster) +python /tools/bin_signing.py --generate-key ecdsa-p256 --out private_key.pem +python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +``` + +2. **Include Public Key in Sketch:** + +```cpp +#include "public_key.h" // Generated by bin_signing.py +``` + +3. **Install Signature Verification:** + +Enable the feature by adding to `build_opt.h`: +``` +-DUPDATE_SIGN +``` + +Then in your sketch: +```cpp +// For RSA with SHA-256 +UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, HASH_SHA256); +Update.installSignature(&sign); + +// For ECDSA with SHA-384 +UpdaterECDSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, HASH_SHA384); +Update.installSignature(&sign); +``` + +4. **Sign Your Firmware:** + +```bash +python /tools/bin_signing.py --bin --key private_key.pem --out firmware_signed.bin --hash +``` + +5. **Upload Signed Application Firmware:** + +The signed firmware includes the signature appended to the binary. Upload the newly created signed firmware instead of the original application binary. + +### Security Best Practices + +1. **Protect Your Private Key:** + - Never commit it to version control + - Store it in secure, encrypted storage + - Limit access to authorized personnel only + - Consider using HSM for production + +2. **Use HTTPS:** + - While signature verification protects integrity, HTTPS protects confidentiality + +## API Reference + +### UpdateClass Methods + +#### begin() +```cpp +bool begin(size_t size = UPDATE_SIZE_UNKNOWN, + int command = U_FLASH, + int ledPin = -1, + uint8_t ledOn = LOW, + const char *label = NULL) +``` +Starts an update operation. + +**Parameters:** +- `size`: Size of the update in bytes (including signature if using signed updates) +- `command`: Update type (U_FLASH, U_SPIFFS, U_FATFS, U_LITTLEFS) +- `ledPin`: Optional LED pin to indicate progress +- `ledOn`: LED on state (LOW or HIGH) +- `label`: Optional partition label + +**Returns:** `true` on success, `false` on failure + +#### installSignature() +```cpp +bool installSignature(UpdaterVerifyClass *sign) +``` +Installs signature verification. Must be called before `begin()`. + +**Parameters:** +- `sign`: Signature verifier (UpdaterRSAVerifier or UpdaterECDSAVerifier) + +**Returns:** `true` on success, `false` on failure + +#### write() +```cpp +size_t write(uint8_t *data, size_t len) +``` +Writes data to the update. + +**Parameters:** +- `data`: Data buffer +- `len`: Length of data + +**Returns:** Number of bytes written + +#### writeStream() +```cpp +size_t writeStream(Stream &data) +``` +Writes data from a stream. + +**Parameters:** +- `data`: Input stream + +**Returns:** Number of bytes written + +#### end() +```cpp +bool end(bool evenIfRemaining = false) +``` +Completes the update and verifies signature if enabled. + +**Parameters:** +- `evenIfRemaining`: Complete even if not all data was written + +**Returns:** `true` if update succeeded and signature is valid, `false` otherwise + +#### abort() +```cpp +void abort() +``` +Aborts the current update. + +#### setMD5() +```cpp +bool setMD5(const char *expected_md5) +``` +Sets expected MD5 hash for verification. + +**Parameters:** +- `expected_md5`: MD5 hash as hex string (32 characters) + +**Returns:** `true` on success, `false` on failure + +#### getError() +```cpp +uint8_t getError() +``` +Returns the last error code. + +**Returns:** Error code (see Error Codes below) + +#### errorString() +```cpp +const char *errorString() +``` +Returns a human-readable error message. + +**Returns:** Error message string + +### Hash Classes (from Hash Library) + +The Update library uses the Hash library for hashing. Simply use the builders from that library: + +```cpp +#include + +SHA256Builder hash256; // SHA-256 +SHA384Builder hash384; // SHA-384 +SHA512Builder hash512; // SHA-512 +``` + +See the [Hash library documentation](../Hash/README.md) for more details. + +### Signature Verifier Classes + +#### UpdaterRSAVerifier +RSA signature verifier. + +```cpp +UpdaterRSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256) +``` + +**Parameters:** +- `pubkey`: Public key in PEM format +- `pubkeyLen`: Length of public key +- `hashType`: Hash algorithm (`HASH_SHA256`, `HASH_SHA384`, or `HASH_SHA512`). Defaults to `HASH_SHA256`. + +#### UpdaterECDSAVerifier +ECDSA signature verifier. + +```cpp +UpdaterECDSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256) +``` + +**Parameters:** +- `pubkey`: Public key in PEM format +- `pubkeyLen`: Length of public key +- `hashType`: Hash algorithm (`HASH_SHA256`, `HASH_SHA384`, or `HASH_SHA512`). Defaults to `HASH_SHA256`. + +### Error Codes + +| Code | Name | Description | +|------|------|-------------| +| 0 | UPDATE_ERROR_OK | No error | +| 1 | UPDATE_ERROR_WRITE | Flash write failed | +| 2 | UPDATE_ERROR_ERASE | Flash erase failed | +| 3 | UPDATE_ERROR_READ | Flash read failed | +| 4 | UPDATE_ERROR_SPACE | Not enough space | +| 5 | UPDATE_ERROR_SIZE | Bad size given | +| 6 | UPDATE_ERROR_STREAM | Stream read timeout | +| 7 | UPDATE_ERROR_MD5 | MD5 check failed | +| 8 | UPDATE_ERROR_MAGIC_BYTE | Wrong magic byte | +| 9 | UPDATE_ERROR_ACTIVATE | Could not activate firmware | +| 10 | UPDATE_ERROR_NO_PARTITION | Partition not found | +| 11 | UPDATE_ERROR_BAD_ARGUMENT | Bad argument | +| 12 | UPDATE_ERROR_ABORT | Aborted | +| 13 | UPDATE_ERROR_DECRYPT | Decryption error | +| 14 | UPDATE_ERROR_SIGN | Signature verification failed | + +## Examples + +- **Signed_OTA_Update**: Demonstrates signed OTA updates with RSA/ECDSA +- **HTTPS_OTA_Update**: HTTPS OTA update +- **HTTP_Client_AES_OTA_Update**: Encrypted OTA update +- **SD_Update**: Update from SD card + +See the `examples/` directory for complete examples. + +## Tools + +### bin_signing.py + +Python script for key generation and firmware signing. Located in `/tools/bin_signing.py`. + +**Requirements:** +```bash +pip install cryptography +``` + +**Usage:** +```bash +# Generate keys +python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem +python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + +# Sign firmware (defaults to SHA-256) +python /tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin + +# Sign firmware with SHA-384 +python /tools/bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384 + +# Verify signature +python /tools/bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem +``` + +See `/tools/bin_signing.py --help` for more options. + +## Troubleshooting + +### "Signature verification failed" + +- Ensure firmware was signed with correct private key +- Verify public key in sketch matches private key +- Check signature scheme and hash algorithm match +- Verify signed binary wasn't corrupted + +### "Failed to install signature verification" + +- Call `installSignature()` before `Update.begin()` +- Ensure hash and sign objects are properly initialized + +### "Update failed" with no specific error + +- Check firmware size is correct (including signature) +- Ensure enough space in target partition +- Verify magic byte (0xE9) at start of firmware + +### Memory Issues + +- Signature verification requires ~2KB of heap +- RSA-4096 uses more memory than ECDSA-P256 +- Ensure sufficient free heap before starting update + +## Compile-Time Options + +The Update library supports compile-time configuration to reduce code size if certain features are not needed: + +### UPDATE_SIGN + +Enable signature verification support (disabled by default). + +Add to your project's `build_opt.h`: +``` +-DUPDATE_SIGN +``` + +Or add to your build flags in `platformio.ini`: +```ini +build_flags = -DUPDATE_SIGN +``` + +**Effects:** +- Enables signature verification classes and methods +- Adds RSA and ECDSA signature verification support +- `installSignature()` method becomes available +- Increases code size due to mbedtls cryptographic functions + +### UPDATE_NOCRYPT + +Disable encryption/decryption support: + +```cpp +#define UPDATE_NOCRYPT +#include +``` + +**Effects:** +- Removes AES encryption support +- Reduces code size +- `setupCrypt()` and related methods will not be available + +**Note:** To enable signature verification while disabling encryption, add to `build_opt.h`: +``` +-DUPDATE_SIGN +-DUPDATE_NOCRYPT +``` + +## License + +This library is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0. + +## Contributing + +Contributions are welcome! Please submit issues and pull requests on GitHub: +https://github.com/espressif/arduino-esp32 + +## Support + +- Documentation: https://docs.espressif.com/ +- Forum: https://esp32.com/ +- GitHub Issues: https://github.com/espressif/arduino-esp32/issues 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/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/examples/Signed_OTA_Update/README.md b/libraries/Update/examples/Signed_OTA_Update/README.md new file mode 100644 index 00000000000..be449f492d4 --- /dev/null +++ b/libraries/Update/examples/Signed_OTA_Update/README.md @@ -0,0 +1,202 @@ +# Signed OTA Update Example + +This example demonstrates how to perform secure OTA (Over-The-Air) updates with signature verification on ESP32 devices using Arduino. + +## Overview + +Code signing ensures that only firmware signed with your private key will be accepted by your devices. This protects against unauthorized firmware updates, even if an attacker gains access to your update server. + +## Features + +- **RSA Signature Verification**: Supports RSA-2048, RSA-3072, and RSA-4096 +- **ECDSA Signature Verification**: Supports ECDSA-P256 and ECDSA-P384 +- **Multiple Hash Algorithms**: SHA-256, SHA-384, and SHA-512 +- **Automatic Signature Verification**: Signatures are verified automatically during OTA update +- **Secure by Default**: Update fails if signature verification fails + +## Prerequisites + +1. **Python 3** with the `cryptography` package: + ```bash + pip install cryptography + ``` + +2. **ESP32 Arduino Core** with Update library + +## Quick Start Guide + +### Step 1: Generate Key Pair + +Generate an RSA-2048 key pair (recommended): +```bash +python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem +python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +``` + +Or generate an ECDSA-P256 key pair (smaller, faster): +```bash +python /tools/bin_signing.py --generate-key ecdsa-p256 --out private_key.pem +python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +``` + +Where `` is your ESP32 Arduino installation path (e.g., `~/Arduino/hardware/espressif/esp32/`). + +**IMPORTANT**: Keep `private_key.pem` secure! Anyone with access to it can sign firmware for your devices. + +### Step 2: Update the Example Sketch + +1. Copy the generated `public_key.h` to the example directory +2. Open `Signed_OTA_Update.ino` +3. Update Wi-Fi credentials: + ```cpp + const char *ssid = "YOUR_SSID"; + const char *password = "YOUR_PASSWORD"; + ``` +4. Update firmware URL: + ```cpp + const char *firmwareUrl = "http://your-server.com/firmware_signed.bin"; + ``` +5. Uncomment the appropriate key type (RSA or ECDSA) +6. Uncomment the appropriate hash algorithm (SHA-256, SHA-384, or SHA-512) + +### Step 3: Build and Upload Initial Firmware + +1. Compile and upload the sketch to your ESP32 +2. Open Serial Monitor to verify it's running + +### Step 4: Build and Sign New Firmware + +1. Make changes to your sketch (e.g., add a version number) +2. Build the sketch and export the binary: + - Arduino IDE: `Sketch` → `Export Compiled Binary` + - Find the application `.bin` file in the `build` folder of your sketch folder. For example `build/espressif.esp32.esp32c6/Signed_OTA_Update.ino.bin`. + +3. Sign the binary: + ```bash + python /tools/bin_signing.py --bin --key private_key.pem --out firmware_signed.bin + ``` + + For other hash algorithms (for example SHA-384): + ```bash + python /tools/bin_signing.py --bin --key private_key.pem --out firmware_signed.bin --hash sha384 + ``` + +### Step 5: Host the Signed Firmware + +Upload `firmware_signed.bin` to your web server and make it accessible at the URL you configured. + +### Step 6: Perform OTA Update + +Reset your ESP32. It will: +1. Connect to Wi-Fi +2. Download the signed firmware +3. Verify the signature +4. Apply the update if signature is valid +5. Reboot with the new firmware + +## Security Considerations + +### Private Key Management + +- **NEVER** commit your private key to version control +- Store it securely (encrypted storage, HSM, etc.) +- Limit access to authorized personnel only +- Consider using separate keys for development and production + +### Recommended Practices + +1. **Use HTTPS**: While signature verification protects firmware integrity, HTTPS protects against MitM attacks +2. **Key Rotation**: Periodically rotate keys (requires firmware update to include new public key) + +## Signature Schemes Comparison + +| Scheme | Key Size | Signature Size | Verification Speed | Security | +|--------|----------|----------------|-------------------|----------| +| RSA-2048 | 2048 bits | 256 bytes | Medium | High | +| RSA-3072 | 3072 bits | 384 bytes | Slower | Very High | +| RSA-4096 | 4096 bits | 512 bytes | Slowest | Maximum | +| ECDSA-P256 | 256 bits | 64 bytes | Fast | High | +| ECDSA-P384 | 384 bits | 96 bytes | Fast | Very High | + +**Recommendation**: RSA-2048 or ECDSA-P256 provide good security with reasonable performance. + +## Hash Algorithms Comparison + +| Algorithm | Output Size | Speed | Security | +|-----------|-------------|-------|----------| +| SHA-256 | 32 bytes | Fast | High | +| SHA-384 | 48 bytes | Medium | Very High | +| SHA-512 | 64 bytes | Medium | Very High | + +**Recommendation**: SHA-256 is sufficient for most applications. + +## Troubleshooting + +### "Signature verification failed" + +- Ensure the firmware was signed with the correct private key +- Verify that the public key in the sketch matches the private key used for signing +- Check that the signature scheme (RSA/ECDSA) and hash algorithm match between signing and verification +- Ensure the signed binary wasn't corrupted during transfer + +### "Failed to install signature verification" + +- Check that `installSignature()` is called before `Update.begin()` +- Ensure hash and sign objects are properly initialized + +### "Public key parsing failed" + +- Verify the public key PEM format is correct +- Ensure PUBLIC_KEY_LEN matches the actual key length + +## Advanced Usage + +### Verifying a Signed Binary + +You can verify a signed binary without flashing it: + +```bash +python bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem +``` + +### Using Different Hash Algorithms + +Match the hash algorithm between signing and verification: + +**Signing with SHA-384:** +```bash +python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384 +``` + +**Sketch configuration:** +```cpp +#define USE_SHA384 +``` + +## API Reference + +### Classes + +- **UpdaterRSAVerifier**: RSA signature verifier +- **UpdaterECDSAVerifier**: ECDSA signature verifier + +### Methods + +```cpp +// Install signature verification (call before Update.begin()) +bool Update.installSignature(UpdaterVerifyClass *sign); +``` + +### Error Codes + +- `UPDATE_ERROR_SIGN (14)`: Signature verification failed + +## License + +This example is part of the Arduino-ESP32 project and is licensed under the Apache License 2.0. + +## Support + +For issues and questions: +- GitHub: https://github.com/espressif/arduino-esp32/issues +- Documentation: https://docs.espressif.com/ diff --git a/libraries/Update/examples/Signed_OTA_Update/Signed_OTA_Update.ino b/libraries/Update/examples/Signed_OTA_Update/Signed_OTA_Update.ino new file mode 100644 index 00000000000..1690d024bfb --- /dev/null +++ b/libraries/Update/examples/Signed_OTA_Update/Signed_OTA_Update.ino @@ -0,0 +1,231 @@ +/* + Signed OTA Update Example + + This example demonstrates how to perform a secure OTA update with signature verification. + Only firmware signed with the correct private key will be accepted. + + NOTE: This example requires signature verification support to be enabled. + This is done automatically via the build_opt.h file in this directory. + + Steps to use this example: + 1. Generate a key pair (see instructions below) + 2. Include the public key in this sketch (see public_key.h) + 3. Build and upload this sketch to your ESP32 + 4. Build your new firmware binary + 5. Sign the binary with the private key (see instructions below) + 6. Upload the signed firmware via OTA (HTTP/HTTPS server) + + Generating keys: + ------------------ + RSA (recommended for maximum compatibility): + python bin_signing.py --generate-key rsa-2048 --out private_key.pem + python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + + ECDSA (smaller keys, faster verification): + python bin_signing.py --generate-key ecdsa-p256 --out private_key.pem + python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + + Signing firmware: + ----------------- + python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin + + IMPORTANT: Keep your private_key.pem secure! Anyone with access to it can + sign firmware that will be accepted by your devices. + + Created by lucasssvaz +*/ + +#include +#include +#include +#include + +// WiFi credentials +const char *ssid = "YOUR_SSID"; +const char *password = "YOUR_PASSWORD"; + +// URL to the signed firmware binary +const char *firmwareUrl = "http://your-server.com/firmware_signed.bin"; + +// Public key for signature verification +// Generated with: python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +// This will create a public_key.h file that you should include below +#include "public_key.h" + +// Uncomment the key type you're using: +#define USE_RSA // RSA signature verification +//#define USE_ECDSA // ECDSA signature verification + +// Uncomment the hash algorithm you're using (must match the one used for signing): +#define USE_SHA256 // SHA-256 (recommended and default) +//#define USE_SHA384 // SHA-384 +//#define USE_SHA512 // SHA-512 + +void performOTAUpdate() { + HTTPClient http; + + Serial.println("Starting OTA update..."); + Serial.print("Firmware URL: "); + Serial.println(firmwareUrl); + + http.begin(firmwareUrl); + int httpCode = http.GET(); + + if (httpCode != HTTP_CODE_OK) { + Serial.printf("HTTP GET failed, error: %s\n", http.errorToString(httpCode).c_str()); + http.end(); + return; + } + + int contentLength = http.getSize(); + Serial.printf("Firmware size: %d bytes\n", contentLength); + + if (contentLength <= 0) { + Serial.println("Invalid content length"); + http.end(); + return; + } + + // The signed firmware includes the signature (512 bytes padding) + // The actual firmware size is contentLength - 512 + const size_t signatureSize = 512; + size_t firmwareSize = contentLength - signatureSize; + + Serial.printf("Actual firmware size: %d bytes\n", firmwareSize); + Serial.printf("Signature size: %d bytes\n", signatureSize); + + // Select hash algorithm +#ifdef USE_SHA256 + int hashType = HASH_SHA256; + Serial.println("Using SHA-256 hash"); +#elif defined(USE_SHA384) + int hashType = HASH_SHA384; + Serial.println("Using SHA-384 hash"); +#elif defined(USE_SHA512) + int hashType = HASH_SHA512; + Serial.println("Using SHA-512 hash"); +#else +#error "Please define a hash algorithm (USE_SHA256, USE_SHA384, or USE_SHA512)" +#endif + + // Create verifier object +#ifdef USE_RSA + UpdaterRSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); + Serial.println("Using RSA signature verification"); +#elif defined(USE_ECDSA) + UpdaterECDSAVerifier sign(PUBLIC_KEY, PUBLIC_KEY_LEN, hashType); + Serial.println("Using ECDSA signature verification"); +#else +#error "Please define a signature scheme (USE_RSA or USE_ECDSA)" +#endif + + // Install signature verification BEFORE calling Update.begin() + if (!Update.installSignature(&sign)) { + Serial.println("Failed to install signature verification"); + http.end(); + return; + } + Serial.println("Signature verification installed"); + + // Begin update with the TOTAL size (firmware + signature) + if (!Update.begin(contentLength)) { + Serial.printf("Update.begin failed: %s\n", Update.errorString()); + http.end(); + return; + } + + // Get the stream + WiFiClient *stream = http.getStreamPtr(); + + // Write firmware data + Serial.println("Writing firmware..."); + size_t written = 0; + uint8_t buff[1024]; + int progress = 0; + + while (http.connected() && (written < contentLength)) { + size_t available = stream->available(); + + if (available) { + int bytesRead = stream->readBytes(buff, min(available, sizeof(buff))); + + if (bytesRead > 0) { + size_t bytesWritten = Update.write(buff, bytesRead); + + if (bytesWritten > 0) { + written += bytesWritten; + + // Print progress + int newProgress = (written * 100) / contentLength; + if (newProgress != progress && newProgress % 10 == 0) { + progress = newProgress; + Serial.printf("Progress: %d%%\n", progress); + } + } else { + Serial.printf("Update.write failed: %s\n", Update.errorString()); + break; + } + } + } + delay(1); + } + + Serial.printf("Written: %d bytes\n", written); + + // End the update - this will verify the signature + if (Update.end()) { + Serial.println("OTA update completed successfully!"); + Serial.println("Signature verified!"); + + if (Update.isFinished()) { + Serial.println("Update successfully completed. Rebooting..."); + delay(1000); + ESP.restart(); + } else { + Serial.println("Update not finished? Something went wrong!"); + } + } else { + Serial.printf("Update.end failed: %s\n", Update.errorString()); + + // Check if it was a signature verification failure + if (Update.getError() == UPDATE_ERROR_SIGN) { + Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + Serial.println("SIGNATURE VERIFICATION FAILED!"); + Serial.println("The firmware was not signed with the"); + Serial.println("correct private key or is corrupted."); + Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } + } + + http.end(); +} + +void setup() { + Serial.begin(115200); + Serial.println("\n\nSigned OTA Update Example"); + Serial.println("=========================\n"); + + // Connect to WiFi + Serial.printf("Connecting to WiFi: %s\n", ssid); + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("\nWiFi connected!"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + // Wait a bit before starting OTA + delay(2000); + + // Perform OTA update + performOTAUpdate(); +} + +void loop() { + // Nothing to do here + delay(1000); +} diff --git a/libraries/Update/examples/Signed_OTA_Update/build_opt.h b/libraries/Update/examples/Signed_OTA_Update/build_opt.h new file mode 100644 index 00000000000..1b328fa2487 --- /dev/null +++ b/libraries/Update/examples/Signed_OTA_Update/build_opt.h @@ -0,0 +1 @@ +-DUPDATE_SIGN diff --git a/libraries/Update/examples/Signed_OTA_Update/ci.yml b/libraries/Update/examples/Signed_OTA_Update/ci.yml new file mode 100644 index 00000000000..006e6e07dda --- /dev/null +++ b/libraries/Update/examples/Signed_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/Signed_OTA_Update/public_key.h b/libraries/Update/examples/Signed_OTA_Update/public_key.h new file mode 100644 index 00000000000..90ff6507a1e --- /dev/null +++ b/libraries/Update/examples/Signed_OTA_Update/public_key.h @@ -0,0 +1,32 @@ +// Public key for OTA signature verification +// Include this in your Arduino sketch + +// ⚠️ THIS IS A TEST KEY - DO NOT USE IN PRODUCTION! +// Generate your own keys using: +// python /tools/bin_signing.py --generate-key rsa-2048 --out private_key.pem +// python /tools/bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +// +// Then replace this file with the generated public_key.h + +// Test RSA-2048 Public Key (PEM format) +const uint8_t PUBLIC_KEY[] PROGMEM = { + 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x42, 0x45, 0x47, 0x49, 0x4e, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x0a, 0x4d, 0x49, 0x49, 0x42, 0x49, 0x6a, 0x41, 0x4e, 0x42, 0x67, 0x6b, 0x71, 0x68, 0x6b, 0x69, 0x47, 0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, + 0x41, 0x4f, 0x43, 0x41, 0x51, 0x38, 0x41, 0x4d, 0x49, 0x49, 0x42, 0x43, 0x67, 0x4b, 0x43, 0x41, 0x51, 0x45, 0x41, 0x36, 0x42, 0x33, 0x52, 0x67, 0x34, 0x39, + 0x6b, 0x4e, 0x47, 0x72, 0x44, 0x2b, 0x50, 0x46, 0x6e, 0x39, 0x64, 0x69, 0x4b, 0x0a, 0x57, 0x50, 0x34, 0x65, 0x42, 0x59, 0x4d, 0x2f, 0x49, 0x79, 0x6b, 0x55, + 0x4b, 0x4d, 0x34, 0x39, 0x63, 0x6a, 0x65, 0x56, 0x56, 0x4f, 0x39, 0x42, 0x4f, 0x30, 0x66, 0x6c, 0x47, 0x47, 0x6e, 0x47, 0x71, 0x79, 0x34, 0x50, 0x72, 0x69, + 0x4e, 0x71, 0x32, 0x62, 0x4a, 0x4a, 0x6a, 0x7a, 0x68, 0x38, 0x46, 0x32, 0x42, 0x53, 0x4f, 0x75, 0x74, 0x48, 0x77, 0x75, 0x7a, 0x6d, 0x62, 0x45, 0x52, 0x6f, + 0x0a, 0x30, 0x38, 0x51, 0x72, 0x32, 0x30, 0x4e, 0x61, 0x52, 0x72, 0x7a, 0x6e, 0x71, 0x6e, 0x59, 0x4e, 0x57, 0x4e, 0x69, 0x6e, 0x43, 0x67, 0x7a, 0x34, 0x34, + 0x49, 0x4e, 0x50, 0x50, 0x78, 0x70, 0x45, 0x55, 0x65, 0x68, 0x61, 0x32, 0x66, 0x6d, 0x6d, 0x39, 0x77, 0x5a, 0x67, 0x57, 0x31, 0x69, 0x31, 0x67, 0x31, 0x77, + 0x70, 0x68, 0x56, 0x51, 0x6c, 0x5a, 0x30, 0x49, 0x63, 0x72, 0x6d, 0x5a, 0x5a, 0x0a, 0x42, 0x61, 0x33, 0x49, 0x64, 0x6a, 0x78, 0x63, 0x52, 0x67, 0x51, 0x6c, + 0x69, 0x32, 0x4b, 0x74, 0x78, 0x72, 0x41, 0x4a, 0x67, 0x33, 0x4a, 0x47, 0x43, 0x54, 0x2f, 0x39, 0x6d, 0x7a, 0x52, 0x31, 0x70, 0x37, 0x59, 0x34, 0x50, 0x34, + 0x65, 0x71, 0x30, 0x6b, 0x2b, 0x78, 0x2b, 0x45, 0x72, 0x6f, 0x35, 0x73, 0x47, 0x69, 0x49, 0x7a, 0x33, 0x44, 0x67, 0x61, 0x50, 0x43, 0x54, 0x41, 0x37, 0x52, + 0x0a, 0x4b, 0x69, 0x75, 0x6e, 0x2f, 0x67, 0x64, 0x56, 0x71, 0x34, 0x35, 0x2f, 0x75, 0x62, 0x64, 0x53, 0x58, 0x65, 0x62, 0x50, 0x46, 0x43, 0x73, 0x36, 0x66, + 0x46, 0x73, 0x52, 0x39, 0x6d, 0x43, 0x6f, 0x37, 0x70, 0x43, 0x4b, 0x74, 0x45, 0x55, 0x51, 0x78, 0x34, 0x4d, 0x68, 0x55, 0x4e, 0x5a, 0x48, 0x48, 0x31, 0x49, + 0x33, 0x62, 0x79, 0x57, 0x35, 0x7a, 0x39, 0x36, 0x49, 0x6a, 0x46, 0x44, 0x68, 0x0a, 0x54, 0x2f, 0x64, 0x5a, 0x71, 0x32, 0x6d, 0x44, 0x54, 0x64, 0x76, 0x59, + 0x2b, 0x6d, 0x5a, 0x75, 0x51, 0x4d, 0x37, 0x6c, 0x72, 0x31, 0x4d, 0x4e, 0x6a, 0x35, 0x36, 0x79, 0x74, 0x41, 0x56, 0x4a, 0x39, 0x56, 0x7a, 0x74, 0x44, 0x75, + 0x35, 0x4f, 0x6a, 0x48, 0x32, 0x76, 0x6f, 0x32, 0x6b, 0x59, 0x46, 0x4f, 0x72, 0x52, 0x49, 0x57, 0x70, 0x5a, 0x4c, 0x56, 0x35, 0x6c, 0x47, 0x79, 0x7a, 0x45, + 0x0a, 0x33, 0x77, 0x49, 0x44, 0x41, 0x51, 0x41, 0x42, 0x0a, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x45, 0x4e, 0x44, 0x20, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x20, + 0x4b, 0x45, 0x59, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x0a, 0x00, +}; +const size_t PUBLIC_KEY_LEN = 452; diff --git a/libraries/Update/keywords.txt b/libraries/Update/keywords.txt index 53544dbaf6c..5ddd5d46c91 100644 --- a/libraries/Update/keywords.txt +++ b/libraries/Update/keywords.txt @@ -1,5 +1,5 @@ ####################################### -# Syntax Coloring Map For Ultrasound +# Syntax Coloring Map For Update ####################################### ####################################### @@ -7,6 +7,9 @@ ####################################### Update KEYWORD1 +UpdaterVerifyClass KEYWORD1 +UpdaterRSAVerifier KEYWORD1 +UpdaterECDSAVerifier KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -17,7 +20,44 @@ end KEYWORD2 write KEYWORD2 writeStream KEYWORD2 printError KEYWORD2 +installSignature KEYWORD2 +setMD5 KEYWORD2 +md5String KEYWORD2 +canRollBack KEYWORD2 +rollBack KEYWORD2 +onProgress KEYWORD2 +abort KEYWORD2 +setupCrypt KEYWORD2 +setCryptKey KEYWORD2 +setCryptMode KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### + +UPDATE_ERROR_OK LITERAL1 +UPDATE_ERROR_WRITE LITERAL1 +UPDATE_ERROR_ERASE LITERAL1 +UPDATE_ERROR_READ LITERAL1 +UPDATE_ERROR_SPACE LITERAL1 +UPDATE_ERROR_SIZE LITERAL1 +UPDATE_ERROR_STREAM LITERAL1 +UPDATE_ERROR_MD5 LITERAL1 +UPDATE_ERROR_MAGIC_BYTE LITERAL1 +UPDATE_ERROR_ACTIVATE LITERAL1 +UPDATE_ERROR_NO_PARTITION LITERAL1 +UPDATE_ERROR_BAD_ARGUMENT LITERAL1 +UPDATE_ERROR_ABORT LITERAL1 +UPDATE_ERROR_DECRYPT LITERAL1 +UPDATE_ERROR_SIGN LITERAL1 +U_FLASH LITERAL1 +U_FLASHFS LITERAL1 +U_SPIFFS LITERAL1 +U_FATFS LITERAL1 +U_LITTLEFS LITERAL1 +SIGN_NONE LITERAL1 +SIGN_RSA LITERAL1 +SIGN_ECDSA LITERAL1 +HASH_SHA256 LITERAL1 +HASH_SHA384 LITERAL1 +HASH_SHA512 LITERAL1 diff --git a/libraries/Update/library.properties b/libraries/Update/library.properties index a192318bb73..e1b77c462b1 100644 --- a/libraries/Update/library.properties +++ b/libraries/Update/library.properties @@ -1,5 +1,5 @@ name=Update -version=3.3.2 +version=3.3.4 author=Hristo Gochkov maintainer=Hristo Gochkov sentence=ESP32 Sketch Update Library diff --git a/libraries/Update/src/Update.h b/libraries/Update/src/Update.h index 5f52e6b3b73..86a1a5f1a26 100644 --- a/libraries/Update/src/Update.h +++ b/libraries/Update/src/Update.h @@ -11,6 +11,9 @@ #include #include #include "esp_partition.h" +#ifdef UPDATE_SIGN +#include "Updater_Signing.h" +#endif /* UPDATE_SIGN */ #define UPDATE_ERROR_OK (0) #define UPDATE_ERROR_WRITE (1) @@ -26,6 +29,7 @@ #define UPDATE_ERROR_BAD_ARGUMENT (11) #define UPDATE_ERROR_ABORT (12) #define UPDATE_ERROR_DECRYPT (13) +#define UPDATE_ERROR_SIGN (14) #define UPDATE_SIZE_UNKNOWN 0xFFFFFFFF @@ -168,6 +172,16 @@ class UpdateClass { return _md5.getBytes(result); } +#ifdef UPDATE_SIGN + /* + Install signature verification for the update + Call this before begin() to enable signature verification + sign: Signature verifier to use (e.g., UpdaterRSAVerifier or UpdaterECDSAVerifier) + The hash type is determined from the verifier's configuration + */ + bool installSignature(UpdaterVerifyClass *sign); +#endif /* UPDATE_SIGN */ + //Helpers uint8_t getError() { return _error; @@ -287,6 +301,14 @@ class UpdateClass { size_t _cryptAddress; uint8_t _cryptCfg; #endif /* UPDATE_NOCRYPT */ + +#ifdef UPDATE_SIGN + SHA2Builder *_hash; + UpdaterVerifyClass *_sign; + uint8_t *_signatureBuffer; + size_t _signatureSize; + int _hashType; +#endif /* UPDATE_SIGN */ }; #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_UPDATE) diff --git a/libraries/Update/src/Updater.cpp b/libraries/Update/src/Updater.cpp index e9f39f729d3..607502069aa 100644 --- a/libraries/Update/src/Updater.cpp +++ b/libraries/Update/src/Updater.cpp @@ -44,6 +44,10 @@ static const char *_err2str(uint8_t _error) { } else if (_error == UPDATE_ERROR_DECRYPT) { return ("Decryption error"); #endif /* UPDATE_NOCRYPT */ +#ifdef UPDATE_SIGN + } else if (_error == UPDATE_ERROR_SIGN) { + return ("Signature Verification Failed"); +#endif /* UPDATE_SIGN */ } return ("UNKNOWN"); } @@ -80,6 +84,10 @@ UpdateClass::UpdateClass() , _cryptMode(U_AES_DECRYPT_AUTO), _cryptAddress(0), _cryptCfg(0xf) #endif /* UPDATE_NOCRYPT */ +#ifdef UPDATE_SIGN + , + _hash(NULL), _sign(NULL), _signatureBuffer(NULL), _signatureSize(0), _hashType(-1) +#endif /* UPDATE_SIGN */ { } @@ -95,6 +103,17 @@ void UpdateClass::_reset() { if (_skipBuffer) { delete[] _skipBuffer; } +#ifdef UPDATE_SIGN + if (_signatureBuffer) { + delete[] _signatureBuffer; + _signatureBuffer = nullptr; + } + if (_hash && _hashType >= 0) { + // Clean up internally-created hash object + delete _hash; + _hash = nullptr; + } +#endif /* UPDATE_SIGN */ #ifndef UPDATE_NOCRYPT _cryptBuffer = nullptr; @@ -105,6 +124,9 @@ void UpdateClass::_reset() { _progress = 0; _size = 0; _command = U_FLASH; +#ifdef UPDATE_SIGN + _signatureSize = 0; +#endif /* UPDATE_SIGN */ if (_ledPin != -1) { digitalWrite(_ledPin, !_ledOn); // off @@ -127,7 +149,39 @@ bool UpdateClass::rollBack() { return _partitionIsBootable(partition) && !esp_ota_set_boot_partition(partition); } +#ifdef UPDATE_SIGN +bool UpdateClass::installSignature(UpdaterVerifyClass *sign) { + if (_size > 0) { + log_w("Update already running"); + return false; + } + if (!sign) { + log_e("Invalid verifier"); + return false; + } + + int hashType = sign->getHashType(); + if (hashType != HASH_SHA256 && hashType != HASH_SHA384 && hashType != HASH_SHA512) { + log_e("Invalid hash type: %d", hashType); + return false; + } + + _sign = sign; + _hashType = hashType; + _signatureSize = 512; // Fixed signature size (padded to 512 bytes) + + [[maybe_unused]] + const char *hashName = (hashType == HASH_SHA256) ? "SHA-256" + : (hashType == HASH_SHA384) ? "SHA-384" + : "SHA-512"; + log_i("Signature verification installed (hash: %s, signature size: %u bytes)", hashName, _signatureSize); + return true; +} +#endif /* UPDATE_SIGN */ + 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; @@ -141,11 +195,41 @@ bool UpdateClass::begin(size_t size, int command, int ledPin, uint8_t ledOn, con _target_md5 = emptyString; _md5 = MD5Builder(); +#ifdef UPDATE_SIGN + // Create and initialize signature hash if signature verification is enabled + if (_sign && _hashType >= 0) { + // Create the appropriate hash builder based on hashType + switch (_hashType) { + case HASH_SHA256: _hash = new SHA256Builder(); break; + case HASH_SHA384: _hash = new SHA384Builder(); break; + case HASH_SHA512: _hash = new SHA512Builder(); break; + default: log_e("Invalid hash type"); return false; + } + + if (_hash) { + _hash->begin(); + log_i("Signature hash initialized"); + } else { + log_e("Failed to create hash builder"); + return false; + } + } +#endif /* UPDATE_SIGN */ + if (size == 0) { _error = UPDATE_ERROR_SIZE; return false; } +#ifdef UPDATE_SIGN + // Validate size is large enough to contain firmware + signature + if (_signatureSize > 0 && size < _signatureSize) { + _error = UPDATE_ERROR_SIZE; + log_e("Size too small for signature: %u < %u", size, _signatureSize); + return false; + } +#endif /* UPDATE_SIGN */ + if (command == U_FLASH) { _partition = esp_ota_get_next_update_partition(NULL); if (!_partition) { @@ -460,6 +544,23 @@ bool UpdateClass::_writeBuffer() { #ifndef UPDATE_NOCRYPT } #endif /* UPDATE_NOCRYPT */ + +#ifdef UPDATE_SIGN + // Add data to signature hash if signature verification is enabled + // Only hash firmware bytes, not the signature bytes at the end + if (_hash && _signatureSize > 0) { + size_t firmwareSize = _size - _signatureSize; + if (_progress < firmwareSize) { + // Calculate how many bytes of this buffer are firmware (not signature) + size_t bytesToHash = _bufferLen; + if (_progress + _bufferLen > firmwareSize) { + bytesToHash = firmwareSize - _progress; + } + _hash->add(_buffer, bytesToHash); + } + } +#endif /* UPDATE_SIGN */ + _progress += _bufferLen; _bufferLen = 0; if (_progress_callback) { @@ -545,6 +646,41 @@ bool UpdateClass::end(bool evenIfRemaining) { } } +#ifdef UPDATE_SIGN + // Verify signature if signature verification is enabled + if (_hash && _sign && _signatureSize > 0) { + log_i("Verifying signature..."); + _hash->calculate(); + + // Allocate buffer for signature (max 512 bytes for RSA-4096) + const size_t maxSigSize = 512; + _signatureBuffer = new (std::nothrow) uint8_t[maxSigSize]; + if (!_signatureBuffer) { + log_e("Failed to allocate signature buffer"); + _abort(UPDATE_ERROR_SIGN); + return false; + } + + // Read signature from partition (last 512 bytes of what was written) + size_t firmwareSize = _size - _signatureSize; + log_d("Reading signature from offset %u (firmware size: %u, total size: %u)", firmwareSize, firmwareSize, _size); + if (!ESP.partitionRead(_partition, firmwareSize, (uint32_t *)_signatureBuffer, maxSigSize)) { + log_e("Failed to read signature from partition"); + _abort(UPDATE_ERROR_SIGN); + return false; + } + + // Verify the signature + if (!_sign->verify(_hash, _signatureBuffer, maxSigSize)) { + log_e("Signature verification failed"); + _abort(UPDATE_ERROR_SIGN); + return false; + } + + log_i("Signature verified successfully"); + } +#endif /* UPDATE_SIGN */ + return _verifyEnd(); } diff --git a/libraries/Update/src/Updater_Signing.cpp b/libraries/Update/src/Updater_Signing.cpp new file mode 100644 index 00000000000..3332fe11942 --- /dev/null +++ b/libraries/Update/src/Updater_Signing.cpp @@ -0,0 +1,138 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifdef UPDATE_SIGN + +#include "Updater_Signing.h" +#include "mbedtls/pk.h" +#include "mbedtls/rsa.h" +#include "mbedtls/ecdsa.h" +#include "mbedtls/ecp.h" +#include "mbedtls/md.h" +#include "esp32-hal-log.h" + +// ==================== UpdaterRSAVerifier (using mbedtls) ==================== + +UpdaterRSAVerifier::UpdaterRSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType) : _hashType(hashType), _valid(false) { + _ctx = new mbedtls_pk_context; + mbedtls_pk_init((mbedtls_pk_context *)_ctx); + + // Try to parse the public key + int ret = mbedtls_pk_parse_public_key((mbedtls_pk_context *)_ctx, pubkey, pubkeyLen); + if (ret != 0) { + log_e("Failed to parse RSA public key: -0x%04X", -ret); + return; + } + + // Verify it's an RSA key + if (mbedtls_pk_get_type((mbedtls_pk_context *)_ctx) != MBEDTLS_PK_RSA) { + log_e("Public key is not RSA"); + return; + } + + _valid = true; + log_i("RSA public key loaded successfully"); +} + +UpdaterRSAVerifier::~UpdaterRSAVerifier() { + if (_ctx) { + mbedtls_pk_free((mbedtls_pk_context *)_ctx); + delete (mbedtls_pk_context *)_ctx; + _ctx = nullptr; + } +} + +bool UpdaterRSAVerifier::verify(SHA2Builder *hash, const void *signature, size_t signatureLen) { + if (!_valid || !hash) { + log_e("Invalid RSA verifier or hash"); + return false; + } + + mbedtls_md_type_t md_type; + switch (_hashType) { + case HASH_SHA256: md_type = MBEDTLS_MD_SHA256; break; + case HASH_SHA384: md_type = MBEDTLS_MD_SHA384; break; + case HASH_SHA512: md_type = MBEDTLS_MD_SHA512; break; + default: log_e("Invalid hash type"); return false; + } + + // Get hash bytes from the builder + uint8_t hashBytes[64]; // Max hash size (SHA-512) + hash->getBytes(hashBytes); + + int ret = mbedtls_pk_verify((mbedtls_pk_context *)_ctx, md_type, hashBytes, hash->getHashSize(), (const unsigned char *)signature, signatureLen); + + if (ret == 0) { + log_i("RSA signature verified successfully"); + return true; + } else { + log_e("RSA signature verification failed: -0x%04X", -ret); + return false; + } +} + +// ==================== UpdaterECDSAVerifier (using mbedtls) ==================== + +UpdaterECDSAVerifier::UpdaterECDSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType) : _hashType(hashType), _valid(false) { + _ctx = new mbedtls_pk_context; + mbedtls_pk_init((mbedtls_pk_context *)_ctx); + + // Try to parse the public key + int ret = mbedtls_pk_parse_public_key((mbedtls_pk_context *)_ctx, pubkey, pubkeyLen); + if (ret != 0) { + log_e("Failed to parse ECDSA public key: -0x%04X", -ret); + return; + } + + // Verify it's an ECDSA key + mbedtls_pk_type_t type = mbedtls_pk_get_type((mbedtls_pk_context *)_ctx); + if (type != MBEDTLS_PK_ECKEY && type != MBEDTLS_PK_ECDSA) { + log_e("Public key is not ECDSA"); + return; + } + + _valid = true; + log_i("ECDSA public key loaded successfully"); +} + +UpdaterECDSAVerifier::~UpdaterECDSAVerifier() { + if (_ctx) { + mbedtls_pk_free((mbedtls_pk_context *)_ctx); + delete (mbedtls_pk_context *)_ctx; + _ctx = nullptr; + } +} + +bool UpdaterECDSAVerifier::verify(SHA2Builder *hash, const void *signature, size_t signatureLen) { + if (!_valid || !hash) { + log_e("Invalid ECDSA verifier or hash"); + return false; + } + + mbedtls_md_type_t md_type; + switch (_hashType) { + case HASH_SHA256: md_type = MBEDTLS_MD_SHA256; break; + case HASH_SHA384: md_type = MBEDTLS_MD_SHA384; break; + case HASH_SHA512: md_type = MBEDTLS_MD_SHA512; break; + default: log_e("Invalid hash type"); return false; + } + + // Get hash bytes from the builder + uint8_t hashBytes[64]; // Max hash size (SHA-512) + hash->getBytes(hashBytes); + + int ret = mbedtls_pk_verify((mbedtls_pk_context *)_ctx, md_type, hashBytes, hash->getHashSize(), (const unsigned char *)signature, signatureLen); + + if (ret == 0) { + log_i("ECDSA signature verified successfully"); + return true; + } else { + log_e("ECDSA signature verification failed: -0x%04X", -ret); + return false; + } +} + +#endif // UPDATE_SIGN diff --git a/libraries/Update/src/Updater_Signing.h b/libraries/Update/src/Updater_Signing.h new file mode 100644 index 00000000000..1134a17e5b4 --- /dev/null +++ b/libraries/Update/src/Updater_Signing.h @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once +#ifdef UPDATE_SIGN + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Signature schemes +#define SIGN_NONE 0 +#define SIGN_RSA 1 +#define SIGN_ECDSA 2 + +// Hash algorithms for signature verification +#define HASH_SHA256 0 +#define HASH_SHA384 1 +#define HASH_SHA512 2 + +// Signature sizes (in bytes) +#define RSA_2048_SIGNATURE_SIZE 256 +#define RSA_3072_SIGNATURE_SIZE 384 +#define RSA_4096_SIGNATURE_SIZE 512 +#define ECDSA_P256_SIGNATURE_SIZE 64 +#define ECDSA_P384_SIGNATURE_SIZE 96 + +// Hash sizes (in bytes) +#define SHA256_SIZE 32 +#define SHA384_SIZE 48 +#define SHA512_SIZE 64 + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus + +class UpdaterVerifyClass { +public: + virtual bool verify(SHA2Builder *hash, const void *signature, size_t signatureLen) = 0; + virtual int getHashType() const = 0; + virtual ~UpdaterVerifyClass() {} +}; + +// Signature verifiers using mbedtls (required for public key cryptography) +class UpdaterRSAVerifier : public UpdaterVerifyClass { +public: + UpdaterRSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256); + ~UpdaterRSAVerifier(); + bool verify(SHA2Builder *hash, const void *signature, size_t signatureLen) override; + int getHashType() const override { + return _hashType; + } + +private: + void *_ctx; + int _hashType; + bool _valid; +}; + +class UpdaterECDSAVerifier : public UpdaterVerifyClass { +public: + UpdaterECDSAVerifier(const uint8_t *pubkey, size_t pubkeyLen, int hashType = HASH_SHA256); + ~UpdaterECDSAVerifier(); + bool verify(SHA2Builder *hash, const void *signature, size_t signatureLen) override; + int getHashType() const override { + return _hashType; + } + +private: + void *_ctx; + int _hashType; + bool _valid; +}; + +#endif // __cplusplus + +#endif // UPDATE_SIGN 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/ci.json b/libraries/WebServer/examples/ChunkWriting/ci.json deleted file mode 100644 index 618e46bd244..00000000000 --- a/libraries/WebServer/examples/ChunkWriting/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/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 e89eb90221c..4d491effc14 100644 --- a/libraries/WebServer/library.properties +++ b/libraries/WebServer/library.properties @@ -1,5 +1,5 @@ name=WebServer -version=3.3.2 +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 e67fcec05e4..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; }); } @@ -613,7 +615,7 @@ void WebServer::chunkResponseEnd() { log_e("Failed to write terminating chunk"); } - _chunkedClient.flush(); + _chunkedClient.clear(); _chunkedResponseActive = false; _chunked = false; _chunkedClient = NetworkClient(); 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/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/ci.json b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json deleted file mode 100644 index 5be7c616d24..00000000000 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "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/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/library.properties b/libraries/WiFi/library.properties index 9c9a267361b..61e8ddb0ca5 100644 --- a/libraries/WiFi/library.properties +++ b/libraries/WiFi/library.properties @@ -1,5 +1,5 @@ name=WiFi -version=3.3.2 +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/WiFi.h b/libraries/WiFi/src/WiFi.h index 6cfeb1155a0..ea2efd97697 100644 --- a/libraries/WiFi/src/WiFi.h +++ b/libraries/WiFi/src/WiFi.h @@ -72,8 +72,6 @@ class WiFiClass : public WiFiGenericClass, public WiFiSTAClass, public WiFiScanC bool isProvEnabled(); }; -#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIFI) extern WiFiClass WiFi; -#endif #endif /* SOC_WIFI_SUPPORTED */ diff --git a/libraries/WiFi/src/WiFiGeneric.cpp b/libraries/WiFi/src/WiFiGeneric.cpp index 599402250dd..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; @@ -263,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; @@ -368,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 @@ -659,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 */ @@ -668,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/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 87bc5691201..0a4aa5f9837 100644 --- a/libraries/WiFiProv/library.properties +++ b/libraries/WiFiProv/library.properties @@ -1,5 +1,5 @@ name=WiFiProv -version=3.3.2 +version=3.3.4 author=Switi Mhaiske maintainer=Hristo Gochkov sentence=Enables provisioning. 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 c4abd55152d..f016c349b88 100644 --- a/libraries/Wire/library.properties +++ b/libraries/Wire/library.properties @@ -1,5 +1,5 @@ name=Wire -version=3.3.2 +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/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_Output/ci.json b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.json deleted file mode 100644 index ceacc367801..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/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_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/ci.json b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.json deleted file mode 100644 index 15d6190e4ae..00000000000 --- a/libraries/Zigbee/examples/Zigbee_Multistate_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_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/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/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/Zigbee_Temp_Hum_Sensor_Sleepy.ino b/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/Zigbee_Temp_Hum_Sensor_Sleepy.ino index 54c085fbfea..401c4ab4552 100644 --- a/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/Zigbee_Temp_Hum_Sensor_Sleepy.ino +++ b/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy/Zigbee_Temp_Hum_Sensor_Sleepy.ino @@ -135,6 +135,9 @@ void setup() { // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement) zbTempSensor.setMinMaxValue(10, 50); + // Set default (initial) value for the temperature sensor to 10.0°C to match the minimum temperature measurement value (default value is 0.0°C) + zbTempSensor.setDefaultValue(10.0); + // Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C) zbTempSensor.setTolerance(1); @@ -142,8 +145,8 @@ void setup() { // The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin() zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35); - // Add humidity cluster to the temperature sensor device with min, max and tolerance values - zbTempSensor.addHumiditySensor(0, 100, 1); + // Add humidity cluster to the temperature sensor device with min, max, tolerance and default values + zbTempSensor.addHumiditySensor(0, 100, 1, 0.0); // Set callback for default response to handle status of reported data, there are 2 options. 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/Zigbee_Temperature_Sensor.ino b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino index ad007abbbaa..e7f4721b47d 100644 --- a/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino +++ b/libraries/Zigbee/examples/Zigbee_Temperature_Sensor/Zigbee_Temperature_Sensor.ino @@ -68,6 +68,9 @@ void setup() { // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement) zbTempSensor.setMinMaxValue(10, 50); + // Optional: Set default (initial) value for the temperature sensor to 10.0°C to match the minimum temperature measurement value + zbTempSensor.setDefaultValue(10.0); + // Optional: Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C) zbTempSensor.setTolerance(1); 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 f203de96da6..9d981dcc39c 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -49,6 +49,7 @@ ZigbeeWindowCoveringType KEYWORD1 ZigbeeFanMode KEYWORD1 ZigbeeFanModeSequence KEYWORD1 zb_cmd_type_t KEYWORD1 +ZigbeeColorMode KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -109,12 +110,23 @@ 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 @@ -142,11 +154,17 @@ 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 @@ -203,6 +221,9 @@ setIASClientEndpoint KEYWORD2 setClosed KEYWORD2 setOpen KEYWORD2 setTilted KEYWORD2 +requestIASZoneEnroll KEYWORD2 +restoreIASZoneEnroll KEYWORD2 +enrolled KEYWORD2 # ZigbeeVibrationSensor setVibration KEYWORD2 @@ -316,3 +337,13 @@ 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 8cb401d713a..cb671149723 100644 --- a/libraries/Zigbee/library.properties +++ b/libraries/Zigbee/library.properties @@ -1,5 +1,5 @@ name=Zigbee -version=3.3.2 +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/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 48a083748f4..742de06835f 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -29,6 +29,7 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) { _cluster_list = nullptr; _on_identify = nullptr; _on_ota_state_change = nullptr; + _on_default_response = nullptr; _read_model = NULL; _read_manufacturer = NULL; _time_status = 0; diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp index cc0222fac42..6955f0e4b20 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp @@ -18,6 +18,7 @@ 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/ZigbeeBinary.cpp b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp index ff99b56091b..b46cbae0b00 100644 --- a/libraries/Zigbee/src/ep/ZigbeeBinary.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp @@ -17,6 +17,7 @@ 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(); diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index 225bb620f0b..711dd1e6879 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -38,6 +38,19 @@ ZigbeeCarbonDioxideSensor::ZigbeeCarbonDioxideSensor(uint8_t endpoint) : ZigbeeE _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}; } +bool ZigbeeCarbonDioxideSensor::setDefaultValue(float defaultValue) { + float zb_default_value = defaultValue / 1000000.0f; + esp_zb_attribute_list_t *carbon_dioxide_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_CARBON_DIOXIDE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = + esp_zb_cluster_update_attr(carbon_dioxide_measure_cluster, ESP_ZB_ZCL_ATTR_CARBON_DIOXIDE_MEASUREMENT_MEASURED_VALUE_ID, (void *)&zb_default_value); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeCarbonDioxideSensor::setMinMaxValue(float min, float max) { float zb_min = min / 1000000.0f; float zb_max = max / 1000000.0f; diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h index bd64e50e51e..2d6a87d946b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h @@ -58,6 +58,10 @@ class ZigbeeCarbonDioxideSensor : public ZigbeeEP { // Set the carbon dioxide value in ppm bool setCarbonDioxide(float carbon_dioxide); + // Set the default (initial) value for the carbon dioxide sensor in ppm + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(float defaultValue); + // Set the min and max value for the carbon dioxide sensor in ppm bool setMinMaxValue(float min, float max); diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp index 3d1b6a57d1b..a31cf174c4e 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp @@ -26,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 @@ -38,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() { @@ -62,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 @@ -75,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 { @@ -85,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); @@ -125,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 */ @@ -179,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 ); @@ -187,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 ); @@ -201,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) { @@ -217,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 5f47002d196..92fbb913f2c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h @@ -64,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); @@ -81,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; @@ -101,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 lightChangedRgb(); + void lightChangedHsv(); + void lightChangedTemp(); + void lightChangedByMode(); - 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); + //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/ZigbeeContactSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp index 4142f87fe16..35e05afb290 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp @@ -31,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(); @@ -44,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; @@ -60,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; @@ -90,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; } @@ -115,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 b3be38c3eb4..002ed722d78 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h @@ -70,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 70e565e32c7..1318cbae082 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp @@ -19,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/ZigbeeDoorWindowHandle.cpp b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp index 2ca032b01e5..2a3a5c80498 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp @@ -31,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(); @@ -108,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; @@ -131,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 d4a2d81eb39..cfaf7e772a0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h @@ -74,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/ZigbeeFanControl.cpp b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp index 45724eb915c..2c9a1f77eca 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp @@ -17,6 +17,7 @@ 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/ZigbeeFlowSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp index ddaee3ea88f..a946f7ad720 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp @@ -36,6 +36,18 @@ ZigbeeFlowSensor::ZigbeeFlowSensor(uint8_t endpoint) : ZigbeeEP(endpoint) { _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}; } +bool ZigbeeFlowSensor::setDefaultValue(float defaultValue) { + uint16_t zb_default_value = (uint16_t)(defaultValue * 10); + esp_zb_attribute_list_t *flow_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_FLOW_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(flow_measure_cluster, ESP_ZB_ZCL_ATTR_FLOW_MEASUREMENT_VALUE_ID, (void *)&zb_default_value); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeFlowSensor::setMinMaxValue(float min, float max) { uint16_t zb_min = (uint16_t)(min * 10); uint16_t zb_max = (uint16_t)(max * 10); diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h index 0402dd15377..0bc7b767bfe 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h @@ -58,6 +58,10 @@ class ZigbeeFlowSensor : public ZigbeeEP { // Set the flow value in 0,1 m3/h bool setFlow(float value); + // Set the default (initial) value for the flow sensor in 0,1 m3/h + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(float defaultValue); + // Set the min and max value for the flow sensor in 0,1 m3/h bool setMinMaxValue(float min, float max); diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp index a2a6d364f9d..351dbbac182 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp @@ -24,6 +24,17 @@ ZigbeeIlluminanceSensor::ZigbeeIlluminanceSensor(uint8_t endpoint) : ZigbeeEP(en _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_LIGHT_SENSOR_DEVICE_ID, .app_device_version = 0}; } +bool ZigbeeIlluminanceSensor::setDefaultValue(uint16_t defaultValue) { + esp_zb_attribute_list_t *light_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(light_measure_cluster, ESP_ZB_ZCL_ATTR_ILLUMINANCE_MEASUREMENT_MEASURED_VALUE_ID, (void *)&defaultValue); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeIlluminanceSensor::setMinMaxValue(uint16_t min, uint16_t max) { esp_zb_attribute_list_t *light_measure_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h index fdc369b4a55..5ae36b0c890 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h @@ -49,6 +49,10 @@ class ZigbeeIlluminanceSensor : public ZigbeeEP { // Set the illuminance value bool setIlluminance(uint16_t value); + // Set the default (initial) value for the illuminance sensor + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(uint16_t defaultValue); + // Set the min and max value for the illuminance sensor bool setMinMaxValue(uint16_t min, uint16_t max); diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.cpp b/libraries/Zigbee/src/ep/ZigbeeLight.cpp index 75cfacd8418..579498c4aa3 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeLight.cpp @@ -17,6 +17,7 @@ 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/ZigbeePM25Sensor.cpp b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp index fc2ca820c9a..ab3f47d81bf 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp @@ -36,6 +36,17 @@ ZigbeePM25Sensor::ZigbeePM25Sensor(uint8_t endpoint) : ZigbeeEP(endpoint) { _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}; } +bool ZigbeePM25Sensor::setDefaultValue(float defaultValue) { + esp_zb_attribute_list_t *pm2_5_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(pm2_5_measure_cluster, ESP_ZB_ZCL_ATTR_PM2_5_MEASUREMENT_MEASURED_VALUE_ID, (void *)&defaultValue); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeePM25Sensor::setMinMaxValue(float min, float max) { esp_zb_attribute_list_t *pm2_5_measure_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PM2_5_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); diff --git a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h index e4b47574704..967ccdca167 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h @@ -58,6 +58,10 @@ class ZigbeePM25Sensor : public ZigbeeEP { // Set the PM2.5 value in 0.1 µg/m³ bool setPM25(float pm25); + // Set the default (initial) value for the PM2.5 sensor in 0.1 µg/m³ + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(float defaultValue); + // Set the min and max value for the PM2.5 sensor in 0.1 µg/m³ bool setMinMaxValue(float min, float max); diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp index 9009c1dd1a3..250bd657315 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp @@ -17,6 +17,7 @@ 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/ZigbeePressureSensor.cpp b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp index 4fdf8600fbe..da356334965 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp @@ -36,6 +36,17 @@ ZigbeePressureSensor::ZigbeePressureSensor(uint8_t endpoint) : ZigbeeEP(endpoint _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}; } +bool ZigbeePressureSensor::setDefaultValue(int16_t defaultValue) { + esp_zb_attribute_list_t *pressure_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(pressure_measure_cluster, ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_VALUE_ID, (void *)&defaultValue); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeePressureSensor::setMinMaxValue(int16_t min, int16_t max) { esp_zb_attribute_list_t *pressure_measure_cluster = esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h index cd38a24d4a0..930846c8721 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h @@ -58,6 +58,10 @@ class ZigbeePressureSensor : public ZigbeeEP { // Set the pressure value in 1 hPa bool setPressure(int16_t value); + // Set the default (initial) value for the pressure sensor in 1 hPa + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(int16_t defaultValue); + // Set the min and max value for the pressure sensor in 1 hPa bool setMinMaxValue(int16_t min, int16_t max); diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp index a81c63aed06..2660dd7b3d6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp @@ -22,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); diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp index 9159464b309..79b39b14c56 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp @@ -22,6 +22,9 @@ ZigbeeTempSensor::ZigbeeTempSensor(uint8_t endpoint) : ZigbeeEP(endpoint) { esp_zb_temperature_sensor_cfg_t temp_sensor_cfg = ESP_ZB_DEFAULT_TEMPERATURE_SENSOR_CONFIG(); _cluster_list = esp_zb_temperature_sensor_clusters_create(&temp_sensor_cfg); + // Set default (initial) value for the temperature sensor to 0.0°C + setDefaultValue(0.0); + _ep_config = { .endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_TEMPERATURE_SENSOR_DEVICE_ID, .app_device_version = 0 }; @@ -49,6 +52,18 @@ bool ZigbeeTempSensor::setMinMaxValue(float min, float max) { return true; } +bool ZigbeeTempSensor::setDefaultValue(float defaultValue) { + int16_t zb_default_value = zb_float_to_s16(defaultValue); + esp_zb_attribute_list_t *temp_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(temp_measure_cluster, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID, (void *)&zb_default_value); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeTempSensor::setTolerance(float tolerance) { // Convert tolerance to ZCL uint16_t uint16_t zb_tolerance = (uint16_t)(tolerance * 100); @@ -126,11 +141,11 @@ bool ZigbeeTempSensor::reportTemperature() { return true; } -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); +void ZigbeeTempSensor::addHumiditySensor(float min, float max, float tolerance, float defaultValue) { + 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 = (uint16_t)(defaultValue * 100); 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); @@ -142,7 +157,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 e7219c17335..edc8812d5ae 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h @@ -31,6 +31,10 @@ class ZigbeeTempSensor : public ZigbeeEP { // Set the temperature value in 0,01°C bool setTemperature(float value); + // Set the default (initial) value for the temperature sensor in 0,01°C + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(float defaultValue); + // Set the min and max value for the temperature sensor in 0,01°C bool setMinMaxValue(float min, float max); @@ -44,7 +48,7 @@ class ZigbeeTempSensor : public ZigbeeEP { bool reportTemperature(); // Add humidity cluster to the temperature sensor device - void addHumiditySensor(float min, float max, float tolerance); + void addHumiditySensor(float min = 0.0, float max = 100.0, float tolerance = 0.1, float defaultValue = 0.0); // Set the humidity value in 0,01% bool setHumidity(float value); diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp index e076aff79f0..bf69ce85717 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp @@ -26,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(); @@ -39,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}; } @@ -100,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"); @@ -122,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); @@ -156,18 +169,56 @@ 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; @@ -248,7 +299,7 @@ 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; memset(&read_req, 0, sizeof(read_req)); @@ -273,11 +324,11 @@ 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; memset(&read_req, 0, sizeof(read_req)); @@ -303,11 +354,11 @@ 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; memset(&read_req, 0, sizeof(read_req)); @@ -334,11 +385,11 @@ 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; memset(&read_req, 0, sizeof(read_req)); @@ -368,7 +419,7 @@ 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); } } @@ -394,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(); @@ -423,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(); @@ -453,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(); @@ -484,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 1f6aef485d6..57a4c934b79 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.h +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.h @@ -48,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(); @@ -63,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; @@ -80,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 218638ed3cb..a9fd437a2c6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp @@ -31,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(); @@ -57,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; @@ -75,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) { @@ -95,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 9757257b5c1..a819be0ba8f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h @@ -65,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; @@ -73,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 672b6254017..85e7efa655d 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp @@ -42,6 +42,18 @@ static uint16_t zb_windspeed_to_u16(float windspeed) { return (uint16_t)(windspeed * 100); } +bool ZigbeeWindSpeedSensor::setDefaultValue(float defaultValue) { + uint16_t zb_default_value = zb_windspeed_to_u16(defaultValue); + esp_zb_attribute_list_t *windspeed_measure_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_WIND_SPEED_MEASUREMENT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(windspeed_measure_cluster, ESP_ZB_ZCL_ATTR_WIND_SPEED_MEASUREMENT_MEASURED_VALUE_ID, (void *)&zb_default_value); + if (ret != ESP_OK) { + log_e("Failed to set default value: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeWindSpeedSensor::setMinMaxValue(float min, float max) { uint16_t zb_min = zb_windspeed_to_u16(min); uint16_t zb_max = zb_windspeed_to_u16(max); diff --git a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h index b5371f8fdef..011cb5e663c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h @@ -56,6 +56,10 @@ class ZigbeeWindSpeedSensor : public ZigbeeEP { // Set the WindSpeed value in 0,01 m/s bool setWindSpeed(float value); + // Set the default (initial) value for the wind speed sensor in 0,01 m/s + // Must be called before adding the EP to Zigbee class. Only effective in factory reset mode (before commissioning) + bool setDefaultValue(float defaultValue); + // Set the min and max value for the WindSpeed sensor bool setMinMaxValue(float min, float max); diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index a51db92ccc9..f4e365753e5 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -62,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/package.json b/package.json index f4b8a6824da..b8e8085d815 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "framework-arduinoespressif32", - "version": "3.3.2", + "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 77b203fc056..716af37fddb 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -28,6 +28,9 @@ { "name": "ESP32-C3 Dev Board" }, + { + "name": "ESP32-C5 Dev Board" + }, { "name": "ESP32-C6 Dev Board" }, @@ -51,12 +54,12 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-07e9bf49-v1" + "version": "idf-release_v5.5-d66ebb86-v1" }, { "packager": "esp32", "name": "xtensa-esp-elf-gcc", - "version": "esp-14.2.0_20250730" + "version": "esp-14.2.0_20251107" }, { "packager": "esp32", @@ -66,7 +69,7 @@ { "packager": "esp32", "name": "riscv32-esp-elf-gcc", - "version": "esp-14.2.0_20250730" + "version": "esp-14.2.0_20251107" }, { "packager": "esp32", @@ -104,125 +107,125 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-07e9bf49-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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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-07e9bf49-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-07e9bf49-v1.zip", - "checksum": "SHA-256:e5ae9e62d781df941128a526e653bb82decde844604b5ccee62efd12c6b2eaa6", - "size": "448900656" + "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_20250730", + "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_20250730/xtensa-esp-elf-14.2.0_20250730-x86_64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:2031cbec81edf5f863afcaba44ce8d05d2a570c48ad514f0d43be7f9419198d8", - "size": "326342823" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-aarch64-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:0ac7087a07b554477f66043b6cd81189b3b15d89d51dc08767b1af469f3dd532", - "size": "322745855" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-arm-linux-gnueabi.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:8a64d968832a3ede4481fcec8140968384942c52f817c4af347401ef024b52a5", - "size": "322129706" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-i586-linux-gnu.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:a9e3b5f516a00b6b580c7c7bb983e536ea76fb39cac1cbc3d0e632c9f790c71c", - "size": "330989568" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-x86_64-apple-darwin.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-x86_64-apple-darwin.tar.gz", - "checksum": "SHA-256:7f6e9273b0c2330869f847dd6a12c7a95eda572de5f58bd5dc4550869395e01f", - "size": "338869041" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-aarch64-apple-darwin.tar.gz", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-aarch64-apple-darwin.tar.gz", - "checksum": "SHA-256:8ce30a1b662dc0d57266146c2a144cb8238761fe8b118c0610ff19533dd35d78", - "size": "321443711" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-i686-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-i686-w64-mingw32.zip", - "checksum": "SHA-256:77020244927a0deaabb650ea1c56ee1d355ca9fdad78d0f53b1a80075ecabcba", - "size": "391479409" + "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_20250730/xtensa-esp-elf-14.2.0_20250730-x86_64-w64-mingw32.zip", - "archiveFileName": "xtensa-esp-elf-14.2.0_20250730-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:9ce39218884cfbf428f9f6b1cb6afd9341e327f5e1a8d1d0369c21ae6e3b0828", - "size": "396050234" + "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" } ] }, @@ -290,63 +293,63 @@ }, { "name": "riscv32-esp-elf-gcc", - "version": "esp-14.2.0_20250730", + "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_20250730/riscv32-esp-elf-14.2.0_20250730-x86_64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-x86_64-linux-gnu.tar.gz", - "checksum": "SHA-256:05b3fe041866c2bee400c3a5bc591beb710795aee3bab63b7c95e2e47f1f4cf4", - "size": "596288860" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-aarch64-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-aarch64-linux-gnu.tar.gz", - "checksum": "SHA-256:e1c63b7dc9348af0a642b1694a7f5b83fdb08ad1fb4ec0742f52864eddfe0573", - "size": "590413157" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-arm-linux-gnueabi.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-arm-linux-gnueabi.tar.gz", - "checksum": "SHA-256:433a232e515a75f9155ab50900cfeb25f6522f40faf9e24a0126d67448e31784", - "size": "589108924" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-i586-linux-gnu.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-i586-linux-gnu.tar.gz", - "checksum": "SHA-256:b28e95b39f64e485da4cb9e2c93904cefeb46ce17390cfe8844cbd96354921d4", - "size": "599653621" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-x86_64-apple-darwin.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-x86_64-apple-darwin.tar.gz", - "checksum": "SHA-256:07fc04efb5ee40c75f526761424d1e8cd1b486393b8edc1dc558a5eaa6524120", - "size": "604869144" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-aarch64-apple-darwin.tar.gz", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-aarch64-apple-darwin.tar.gz", - "checksum": "SHA-256:654e2c0d14e8c8955a166a800af9081bd2fdad41a89c20659a20eeff6c0f287b", - "size": "581049206" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-i686-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-i686-w64-mingw32.zip", - "checksum": "SHA-256:ac8816920e0bc6c4032abc27f4962b2b2f59b231ed86e0002476196f9f1f0d35", - "size": "686131875" + "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_20250730/riscv32-esp-elf-14.2.0_20250730-x86_64-w64-mingw32.zip", - "archiveFileName": "riscv32-esp-elf-14.2.0_20250730-x86_64-w64-mingw32.zip", - "checksum": "SHA-256:a41e5219f0ff66cde3da3ac096b55d60fef4a1fb746590893c4c47f5437f192a", - "size": "692761883" + "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" } ] }, diff --git a/platform.txt b/platform.txt index a9258e8d7a5..69331b43204 100644 --- a/platform.txt +++ b/platform.txt @@ -1,5 +1,5 @@ name=ESP32 Arduino -version=3.3.2 +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 @@ -189,6 +195,36 @@ recipe.size.pattern="{compiler.path}{compiler.size.cmd}" -A "{build.path}/{build recipe.size.regex=^(?:\.iram0\.text|\.iram0\.vectors|\.dram0\.data|\.dram1\.data|\.flash\.text|\.flash\.rodata|\.flash\.appdesc|\.flash\.init_array|\.eh_frame|)\s+([0-9]+).* recipe.size.regex.data=^(?:\.dram0\.data|\.dram0\.bss|\.dram1\.data|\.dram1\.bss|\.noinit)\s+([0-9]+).* +## Export extra build artifacts when "Export compiled Binary" is selected + +## By default Arduino IDE exports everything that starts with the sketch name +## We need to export other files as well + +## Define FQBN transformation logic as reusable properties +## Converts FQBN to vendor.arch.board format matching Arduino IDE default export path +recipe.hooks.savehex.fqbn_to_dir=FQBN_DIR=$(echo '{build.fqbn}' | cut -d: -f1-3 | tr ':' '.') +recipe.hooks.savehex.fqbn_to_dir.windows=$env:FQBN_DIR=('{build.fqbn}' -split ':')[0..2] -join '.' + +## Export partitions.csv (for reference) +recipe.hooks.savehex.postsavehex.1.pattern=/usr/bin/env bash -c "{recipe.hooks.savehex.fqbn_to_dir} && cp '{build.path}/partitions.csv' '{sketch_path}/build/'$FQBN_DIR'/partitions.csv' 2>/dev/null || true" +recipe.hooks.savehex.postsavehex.1.pattern.windows=powershell -Command "{recipe.hooks.savehex.fqbn_to_dir}; Copy-Item -ErrorAction SilentlyContinue '{build.path}\partitions.csv' ('{sketch_path}\build\' + $env:FQBN_DIR + '\partitions.csv')" + +## Export sdkconfig (for reference) +recipe.hooks.savehex.postsavehex.2.pattern=/usr/bin/env bash -c "{recipe.hooks.savehex.fqbn_to_dir} && cp '{build.path}/sdkconfig' '{sketch_path}/build/'$FQBN_DIR'/sdkconfig' 2>/dev/null || true" +recipe.hooks.savehex.postsavehex.2.pattern.windows=powershell -Command "{recipe.hooks.savehex.fqbn_to_dir}; Copy-Item -ErrorAction SilentlyContinue '{build.path}\sdkconfig' ('{sketch_path}\build\' + $env:FQBN_DIR + '\sdkconfig')" + +## Export flash_args +recipe.hooks.savehex.postsavehex.3.pattern=/usr/bin/env bash -c "{recipe.hooks.savehex.fqbn_to_dir} && cp '{build.path}/flash_args' '{sketch_path}/build/'$FQBN_DIR'/flash_args' 2>/dev/null || true" +recipe.hooks.savehex.postsavehex.3.pattern.windows=powershell -Command "{recipe.hooks.savehex.fqbn_to_dir}; Copy-Item -ErrorAction SilentlyContinue '{build.path}\flash_args' ('{sketch_path}\build\' + $env:FQBN_DIR + '\flash_args')" + +## Export boot_app0.bin +recipe.hooks.savehex.postsavehex.4.pattern=/usr/bin/env bash -c "{recipe.hooks.savehex.fqbn_to_dir} && cp '{runtime.platform.path}/tools/partitions/boot_app0.bin' '{sketch_path}/build/'$FQBN_DIR'/boot_app0.bin' 2>/dev/null || true" +recipe.hooks.savehex.postsavehex.4.pattern.windows=powershell -Command "{recipe.hooks.savehex.fqbn_to_dir}; Copy-Item -ErrorAction SilentlyContinue '{runtime.platform.path}\tools\partitions\boot_app0.bin' ('{sketch_path}\build\' + $env:FQBN_DIR + '\boot_app0.bin')" + +## Export build.options.json +recipe.hooks.savehex.postsavehex.5.pattern=/usr/bin/env bash -c "{recipe.hooks.savehex.fqbn_to_dir} && cp '{build.path}/build.options.json' '{sketch_path}/build/'$FQBN_DIR'/build.options.json' 2>/dev/null || true" +recipe.hooks.savehex.postsavehex.5.pattern.windows=powershell -Command "{recipe.hooks.savehex.fqbn_to_dir}; Copy-Item -ErrorAction SilentlyContinue '{build.path}\build.options.json' ('{sketch_path}\build\' + $env:FQBN_DIR + '\build.options.json')" + ## Required discoveries and monitors ## --------------------------------- pluggable_discovery.required.0=builtin:serial-discovery @@ -352,6 +388,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 f314ebcfdfa..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, 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 c7df59bb3aa..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, 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 bd6c52cac17..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, 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 d4c333d8e70..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, 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 e981565f0ca..00000000000 --- a/tests/performance/psramspeed/ci.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "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/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 68467478eba..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, 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 987b6c00066..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, 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 4e99bbb1c1b..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, 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 29b7d531bd4..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==2.1.0 -pytest-embedded-arduino==2.1.0 -pytest-embedded-wokwi==2.1.0 -pytest-embedded-qemu==2.1.0 +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..20c62fcd351 100644 --- a/tests/validation/gpio/gpio.ino +++ b/tests/validation/gpio/gpio.ino @@ -1,31 +1,231 @@ +/** + * 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; + +// 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; + argInterruptCounter = 0; + argInterruptFlag = false; + receivedArg = 0; +} + +void tearDown(void) {} + +void IRAM_ATTR buttonISR() { + interruptCounter = interruptCounter + 1; + interruptFlag = true; +} + +void IRAM_ATTR buttonISRWithArg(void *arg) { + argInterruptCounter = argInterruptCounter + 1; + argInterruptFlag = true; + receivedArg = *(int *)arg; +} + +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 42010ab520b..30f32562c88 100644 --- a/tests/validation/gpio/test_gpio.py +++ b/tests/validation/gpio/test_gpio.py @@ -1,17 +1,114 @@ import logging from pytest_embedded_wokwi import Wokwi from pytest_embedded import Dut +from time import sleep def test_gpio(dut: Dut, wokwi: Wokwi): LOGGER = logging.getLogger(__name__) - LOGGER.info("Waiting for Button test begin...") - dut.expect_exact("Button test") - - for i in range(3): - LOGGER.info(f"Setting button pressed for {i + 1} seconds") + 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") - dut.expect_exact(f"Button pressed {i + 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.") + + 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 + + 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/i2c_master/i2c_master.ino b/tests/validation/i2c_master/i2c_master.ino index 41e7d2ae5f9..287f4dca76a 100644 --- a/tests/validation/i2c_master/i2c_master.ino +++ b/tests/validation/i2c_master/i2c_master.ino @@ -108,7 +108,7 @@ void ds1307_get_time(uint8_t *sec, uint8_t *min, uint8_t *hour, uint8_t *day, ui void ds1307_set_time(uint8_t sec, uint8_t min, uint8_t hour, uint8_t day, uint8_t month, uint16_t year) { Wire.beginTransmission(DS1307_ADDR); Wire.write(0x00); - Wire.write(DEC2BCD(sec)); + Wire.write(DEC2BCD(sec) | 0x80); //Set halt bit to stop clock Wire.write(DEC2BCD(min)); Wire.write(DEC2BCD(hour)); Wire.write(DEC2BCD(0)); //Ignore day of week @@ -212,6 +212,22 @@ void change_clock() { TEST_ASSERT_EQUAL(start_day, read_day); TEST_ASSERT_EQUAL(start_month, read_month); TEST_ASSERT_EQUAL(start_year, read_year); + + //Run clock for 5 seconds to check that we can write + ds1307_start(); + delay(5000); + ds1307_stop(); + + //Get time + ds1307_get_time(&read_sec, &read_min, &read_hour, &read_day, &read_month, &read_year); + + //Check time + TEST_ASSERT_NOT_EQUAL(start_sec, read_sec); //Seconds should have changed + TEST_ASSERT_EQUAL(start_min, read_min); + TEST_ASSERT_EQUAL(start_hour, read_hour); + TEST_ASSERT_EQUAL(start_day, read_day); + TEST_ASSERT_EQUAL(start_month, read_month); + TEST_ASSERT_EQUAL(start_year, read_year); } void swap_pins() { 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..5ca314eca6d --- /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,ChipVariant=postv3,PartitionScheme=huge_app,FlashMode=dio + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,ChipVariant=postv3,PartitionScheme=huge_app,FlashMode=dio,FlashFreq=40 + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,ChipVariant=postv3,PartitionScheme=huge_app,FlashMode=qio + - espressif:esp32:esp32p4:PSRAM=enabled,USBMode=default,ChipVariant=postv3,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..a5625fc9fa1 --- /dev/null +++ b/tests/validation/periman/ci.yml @@ -0,0 +1,3 @@ +platforms: + qemu: false + wokwi: false diff --git a/tests/validation/periman/periman.ino b/tests/validation/periman/periman.ino index 8da59dd23b9..64d4359f135 100644 --- a/tests/validation/periman/periman.ino +++ b/tests/validation/periman/periman.ino @@ -10,6 +10,8 @@ * - ETH: ETH requires a ethernet port to be connected before the pins are attached */ +#include + #if SOC_I2S_SUPPORTED #include "ESP_I2S.h" #endif @@ -69,9 +71,10 @@ void setup_test(String test_name, int8_t rx_pin = UART1_RX_DEFAULT, int8_t tx_pi uart1_tx_pin = tx_pin; test_executed = false; - pinMode(uart1_rx_pin, INPUT_PULLUP); - pinMode(uart1_tx_pin, OUTPUT); + // Ensure Serial1 is initialized and callback is set (in case it was terminated previously) Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); uart_internal_loopback(1, uart1_rx_pin); delay(100); log_v("Running %s test", test_name.c_str()); @@ -81,13 +84,17 @@ void setup_test(String test_name, int8_t rx_pin = UART1_RX_DEFAULT, int8_t tx_pi void teardown_test(void) { log_v("Tearing down %s test", current_test.c_str()); if (test_executed) { + // Test 1: Peripheral manager auto-detach via pinMode pinMode(uart1_rx_pin, INPUT_PULLUP); pinMode(uart1_tx_pin, OUTPUT); Serial1.print(current_test); Serial1.println(" test: This should not be printed"); Serial1.flush(); + // Restore Serial1 via peripheral manager Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); uart_internal_loopback(1, uart1_rx_pin); delay(100); } @@ -125,7 +132,35 @@ void sigmadelta_test(void) { Serial.println("SigmaDelta init failed"); } #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_SDM_SUPPORTED + // Now test manual deinit path + setup_test("SigmaDelta_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + if (!sigmaDeltaAttach(uart1_rx_pin, 312500)) { + Serial.println("SigmaDelta init failed"); + } + if (!sigmaDeltaAttach(uart1_tx_pin, 312500)) { + Serial.println("SigmaDelta init failed"); + } + + // Manual deinit + sigmaDeltaDetach(uart1_rx_pin); + sigmaDeltaDetach(uart1_tx_pin); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("SigmaDelta_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void adc_oneshot_test(void) { @@ -144,9 +179,9 @@ void adc_oneshot_test(void) { } #if SOC_ADC_SUPPORTED -volatile bool adc_coversion_done = false; +volatile bool adc_conversion_done = false; void ARDUINO_ISR_ATTR adcComplete() { - adc_coversion_done = true; + adc_conversion_done = true; } #endif @@ -158,7 +193,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); @@ -166,7 +201,7 @@ void adc_continuous_test(void) { analogContinuous(adc_pins, adc_pins_count, 6, 20000, &adcComplete); analogContinuousStart(); - while (adc_coversion_done == false) { + while (adc_conversion_done == false) { delay(1); } @@ -176,7 +211,43 @@ void adc_continuous_test(void) { analogContinuousStop(); #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_ADC_SUPPORTED + // Now test manual deinit path + setup_test("ADC_Continuous_deinit", ADC1_DEFAULT, ADC2_DEFAULT); + test_executed = false; // Skip the pinMode test in teardown + adc_conversion_done = false; // Reset flag + + analogContinuousSetWidth(12); + analogContinuousSetAtten(ADC_11db); + analogContinuous(adc_pins, adc_pins_count, 6, 20000, &adcComplete); + analogContinuousStart(); + + while (adc_conversion_done == false) { + delay(1); + } + + if (!analogContinuousRead(&result, 0)) { + Serial.println("ADC continuous read failed"); + } + + analogContinuousStop(); + + // Manual deinit + analogContinuousDeinit(); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("ADC_Continuous_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void dac_test(void) { @@ -202,7 +273,35 @@ void ledc_test(void) { Serial.println("LEDC init failed"); } #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_LEDC_SUPPORTED + // Now test manual deinit path + setup_test("LEDC_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + if (!ledcAttach(uart1_rx_pin, 5000, 12)) { + Serial.println("LEDC init failed"); + } + if (!ledcAttach(uart1_tx_pin, 5000, 12)) { + Serial.println("LEDC init failed"); + } + + // Manual deinit + ledcDetach(uart1_rx_pin); + ledcDetach(uart1_tx_pin); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("LEDC_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void rmt_test(void) { @@ -216,7 +315,35 @@ void rmt_test(void) { Serial.println("RMT init failed"); } #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_RMT_SUPPORTED + // Now test manual deinit path + setup_test("RMT_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + if (!rmtInit(uart1_rx_pin, RMT_TX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { + Serial.println("RMT init failed"); + } + if (!rmtInit(uart1_tx_pin, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_1, 10000000)) { + Serial.println("RMT init failed"); + } + + // Manual deinit + rmtDeinit(uart1_rx_pin); + rmtDeinit(uart1_tx_pin); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("RMT_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void i2s_test(void) { @@ -231,7 +358,34 @@ void i2s_test(void) { Serial.println("I2S init failed"); } #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_I2S_SUPPORTED + // Now test manual deinit path + setup_test("I2S_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + I2SClass i2s2; + i2s2.setPins(uart1_rx_pin, uart1_tx_pin, -1); + i2s2.setTimeout(1000); + if (!i2s2.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) { + Serial.println("I2S init failed"); + } + + // Manual deinit + i2s2.end(); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("I2S_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void i2c_test(void) { @@ -242,7 +396,31 @@ void i2c_test(void) { Serial.println("I2C init failed"); } #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_I2C_SUPPORTED + // Now test manual deinit path + setup_test("I2C_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + if (!Wire.begin(uart1_rx_pin, uart1_tx_pin)) { + Serial.println("I2C init failed"); + } + + // Manual deinit + Wire.end(); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("I2C_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void spi_test(void) { @@ -251,7 +429,29 @@ void spi_test(void) { test_executed = true; SPI.begin(uart1_rx_pin, uart1_tx_pin, -1, -1); #endif - teardown_test(); + teardown_test(); // Tests auto-detach via pinMode + +#if SOC_GPSPI_SUPPORTED + // Now test manual deinit path + setup_test("SPI_deinit"); + test_executed = false; // Skip the pinMode test in teardown + + SPI.begin(uart1_rx_pin, uart1_tx_pin, -1, -1); + + // Manual deinit + SPI.end(); + + // Verify Serial1 can be restored after manual deinit + Serial1.setPins(uart1_rx_pin, uart1_tx_pin); + Serial1.begin(115200); + Serial1.onReceive(onReceive_cb); + uart_internal_loopback(1, uart1_rx_pin); + delay(100); + + Serial1.print("SPI_deinit"); + Serial1.println(" test: This should be printed"); + Serial1.flush(); +#endif } void touch_test(void) { diff --git a/tests/validation/periman/test_periman.py b/tests/validation/periman/test_periman.py index 2728abcef80..56be51406ca 100644 --- a/tests/validation/periman/test_periman.py +++ b/tests/validation/periman/test_periman.py @@ -3,21 +3,31 @@ def test_periman(dut): LOGGER = logging.getLogger(__name__) - peripherals = [ - "GPIO", - "SigmaDelta", - "LEDC", - "RMT", - "I2S", - "I2C", - "SPI", - "ADC_Oneshot", - "ADC_Continuous", - "DAC", - "Touch", + + # Define peripherals and whether they have a manual deinit test + # Format: (name, has_deinit_test) + PERIPHERALS = [ + ("GPIO", False), + ("SigmaDelta", True), + ("LEDC", True), + ("RMT", True), + ("I2S", True), + ("I2C", True), + ("SPI", True), + ("ADC_Oneshot", False), + ("ADC_Continuous", True), + ("DAC", False), + ("Touch", False), ] - pattern = rb"(?:\b\w+\b test: This should(?: not)? be printed|Peripheral Manager test done)" + # Build expected test names + pending_tests = set() + for name, has_deinit in PERIPHERALS: + pending_tests.add(name) + if has_deinit: + pending_tests.add(f"{name}_deinit") + + pattern = rb"(?:\b[\w_]+\b test: This should(?: not)? be printed|Peripheral Manager test done)" while True: try: @@ -26,17 +36,19 @@ def test_periman(dut): assert False, "Could not detect end of test" console_output = res.group(0).decode("utf-8") - peripheral = console_output.split()[0] + test_name = console_output.split()[0] if "Peripheral Manager test done" in console_output: break - if peripheral in peripherals: + if test_name in pending_tests: if "not" in console_output: - assert False, f"Output printed when it should not after peripheral {peripheral}" - LOGGER.info(f"Correct output after peripheral: {peripheral}") - peripherals.remove(peripheral) + assert False, f"Output printed when it should not for test: {test_name}" + else: + test_type = "manual deinit" if test_name.endswith("_deinit") else "auto-detach" + LOGGER.info(f"✓ {test_type} works for: {test_name}") + pending_tests.remove(test_name) else: - assert False, f"Unknown peripheral: {peripheral}" + assert False, f"Unknown test: {test_name}" - assert peripherals == [], f"Missing output after peripherals: {peripherals}" + assert not pending_tests, f"Missing tests: {sorted(pending_tests)}" diff --git a/tests/validation/psram/ci.json b/tests/validation/psram/ci.json deleted file mode 100644 index 4d426d38c30..00000000000 --- a/tests/validation/psram/ci.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "soc_tags": { - "esp32": [ - "psram" - ], - "esp32s2": [ - "psram" - ], - "esp32s3": [ - "octal_psram" - ], - "esp32c5": [ - "psram" - ] - }, - "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/sdcard/ci.yml b/tests/validation/sdcard/ci.yml new file mode 100644 index 00000000000..3f53d32a04e --- /dev/null +++ b/tests/validation/sdcard/ci.yml @@ -0,0 +1,3 @@ +platforms: + hardware: false + qemu: false diff --git a/tests/validation/sdcard/diagram.esp32.json b/tests/validation/sdcard/diagram.esp32.json new file mode 100644 index 00000000000..c1a084b98ab --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-devkit-c-v4", + "id": "esp32", + "top": 27.66, + "left": -12.7, + "rotate": 270, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -74.23, + "left": 16.27, + "rotate": 90, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": 237.23, + "left": -2.27, + "rotate": 270, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:18", "green", [ "v67.2", "h-19.19" ] ], + [ "sd1:GND", "esp32:GND.1", "black", [ "v57.6", "h115.09", "v134.4", "h-96" ] ], + [ "sd1:DO", "esp32:19", "green", [ "v76.8", "h-48.11" ] ], + [ "sd1:DI", "esp32:23", "green", [ "v48", "h-67.11" ] ], + [ "sd1:CS", "esp32:5", "green", [ "v57.6", "h19.14" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v38.4", "h-124.94", "v144.15" ] ], + [ "sd2:SCK", "esp32:25", "green", [ "v0" ] ], + [ "sd2:DO", "esp32:26", "green", [ "v-38.4", "h28.91" ] ], + [ "sd2:DI", "esp32:27", "green", [ "v0" ] ], + [ "sd2:CS", "esp32:14", "green", [ "v0" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "v-28.8", "h-57.46" ] ], + [ "esp32:GND.1", "sd2:GND", "black", [ "v38.25", "h-57.6" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32c3.json b/tests/validation/sdcard/diagram.esp32c3.json new file mode 100644 index 00000000000..da9227af98c --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32c3.json @@ -0,0 +1,33 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-c3-devkitm-1", + "id": "esp32", + "top": -57.6, + "left": -177.56, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": 18.77, + "left": 4.33, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:4", "green", [ "v0.01", "h-142.56" ] ], + [ "sd1:DO", "esp32:5", "green", [ "h-67.2", "v-28.91", "h-75.36" ] ], + [ "sd1:DI", "esp32:6", "green", [ "v0.09", "h-142.56" ] ], + [ "sd1:CS", "esp32:7", "green", [ "h-57.66", "v-9.3" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v11", "h-124.94", "v132.15" ] ], + [ "sd1:GND", "esp32:GND.7", "black", [ "h0" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32c6.json b/tests/validation/sdcard/diagram.esp32c6.json new file mode 100644 index 00000000000..9c846ea918f --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32c6.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-c6-devkitc-1", + "id": "esp32", + "top": -52.31, + "left": -178.28, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -0.43, + "left": 61.93, + "rotate": 180, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": -86.83, + "left": 61.93, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:SCK", "esp32:21", "green", [ "v0.01", "h-96", "v-28.8" ] ], + [ "sd1:DO", "esp32:20", "green", [ "v-0.11", "h-57.6", "v-9.6" ] ], + [ "sd1:DI", "esp32:19", "green", [ "v0.09", "h-86.4", "v38.4" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v-0.14", "h-48", "v-124.8", "h-192" ] ], + [ "esp32:18", "sd2:CS", "green", [ "h56.5", "v-144" ] ], + [ "esp32:19", "sd2:DI", "green", [ "h66.1", "v-124.8" ] ], + [ "esp32:21", "sd2:SCK", "green", [ "h75.7", "v-86.4" ] ], + [ "sd1:GND", "esp32:GND.2", "black", [ "h-19.2", "v-0.11" ] ], + [ "esp32:GND.2", "sd2:GND", "black", [ "h133.3", "v-153.6" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "h-48", "v-38.54", "h-192" ] ], + [ "esp32:20", "sd2:DO", "green", [ "h94.9", "v-76.8", "h-9.6" ] ], + [ "sd1:CS", "esp32:9", "green", [ "h-105.6", "v-0.06" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32h2.json b/tests/validation/sdcard/diagram.esp32h2.json new file mode 100644 index 00000000000..2bd0d65f8f3 --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32h2.json @@ -0,0 +1,36 @@ +{ + "version": 1, + "author": "lucasssvaz", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-h2-devkitm-1", + "id": "esp32", + "top": -55.37, + "left": -177.9, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -151.03, + "left": -60.53, + "rotate": 90, + "attrs": {} + } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:DI", "esp32:6", "green", [ "v0.09", "h-142.56" ] ], + [ "sd1:CS", "esp32:7", "green", [ "h-57.66", "v-9.3" ] ], + [ "sd1:GND", "esp32:GND.7", "black", [ "h0" ] ], + [ "esp32:10", "sd1:SCK", "green", [ "h65.38", "v-74.57" ] ], + [ "sd1:GND", "esp32:GND.6", "black", [ "h-0.11", "v48" ] ], + [ "esp32:25", "sd1:DI", "green", [ "h46.18", "v-93.77" ] ], + [ "sd1:DO", "esp32:11", "green", [ "h-0.11", "v84.17" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v9.6", "h-0.14" ] ], + [ "esp32:0", "sd1:CS", "green", [ "h-19.2", "v-48", "h144" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32p4.json b/tests/validation/sdcard/diagram.esp32p4.json new file mode 100644 index 00000000000..1756f0ef2d1 --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32p4.json @@ -0,0 +1,51 @@ +{ + "version": 1, + "author": "lucasssvaz", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-p4-function-ev", + "id": "esp", + "top": 164.08, + "left": -76.03, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": -26.23, + "left": 112.27, + "rotate": 90, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": -26.23, + "left": 237.07, + "rotate": 90, + "attrs": {} + } + ], + "connections": [ + [ "esp:37", "$serialMonitor:RX", "", [] ], + [ "esp:38", "$serialMonitor:TX", "", [] ], + [ "sd1:CS", "esp:10", "green", [ "h57.6", "v-38.16" ] ], + [ "sd1:DI", "esp:11", "green", [ "h48", "v-19.11" ] ], + [ "sd1:SCK", "esp:12", "green", [ "h38.4", "v9.77" ] ], + [ "sd1:DO", "esp:13", "green", [ "h67.2", "v38.69" ] ], + [ "sd1:VCC", "esp:3V3.1", "red", [ "v19.2", "h-105.74", "v115.2" ] ], + [ "sd2:VCC", "esp:3V3.1", "red", [ "v19.2", "h-230.54", "v115.2" ] ], + [ "esp:GND.1", "sd1:GND", "black", [ "v-9.6", "h-76.8", "v-86.4", "h115.2" ] ], + [ "esp:GND.1", "sd2:GND", "black", [ "v-9.6", "h-76.8", "v-86.4", "h240" ] ], + [ "esp:26", "sd2:CS", "green", [ "v0" ] ], + [ "esp:33", "sd2:DO", "green", [ "v-19.2", "h57.71" ] ], + [ "esp:36", "sd2:SCK", "green", [ "v-19.2", "h67.19" ] ], + [ "esp:32", "sd2:DI", "green", [ "v-28.8", "h38.31" ] ], + [ "esp:1", "sd1:SCK", "green", [ "v-19.2", "h-48" ] ], + [ "esp:2", "sd1:DO", "green", [ "v9.6", "h-124.8", "v-96", "h96.11" ] ], + [ "esp:3", "sd1:DI", "green", [ "v19.2", "h-124.8", "v-115.2", "h67.2", "v9.6" ] ], + [ "esp:8", "sd1:CS", "green", [ "v28.8", "h-67.2", "v-153.6", "h67.2" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32s2.json b/tests/validation/sdcard/diagram.esp32s2.json new file mode 100644 index 00000000000..f7f03bfbd2a --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32s2.json @@ -0,0 +1,40 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { + "type": "board-esp32-s2-devkitm-1", + "id": "esp32", + "top": -61.91, + "left": -177.83, + "attrs": {} + }, + { + "type": "wokwi-microsd-card", + "id": "sd1", + "top": 9.17, + "left": 23.53, + "rotate": 180, + "attrs": {} + }, + { "type": "wokwi-microsd-card", "id": "sd2", "top": -57.37, "left": -345.53, "attrs": {} } + ], + "connections": [ + [ "esp32:RX", "$serialMonitor:TX", "", [] ], + [ "esp32:TX", "$serialMonitor:RX", "", [] ], + [ "sd1:GND", "esp32:GND.1", "black", [ "v-0.11", "h-96", "v115.2", "h-105.33" ] ], + [ "sd1:VCC", "esp32:3V3", "red", [ "v-0.14", "h-38.4", "v-105.6", "h-162.93" ] ], + [ "sd1:CS", "esp32:34", "green", [ "h-86.4", "v-0.06" ] ], + [ "sd1:DI", "esp32:35", "green", [ "h-76.8", "v0.09" ] ], + [ "sd1:DO", "esp32:37", "green", [ "h0" ] ], + [ "sd1:SCK", "esp32:36", "green", [ "h-48", "v28.81" ] ], + [ "esp32:1", "sd2:SCK", "green", [ "h0" ] ], + [ "esp32:2", "sd2:DO", "green", [ "h-48", "v-19.2" ] ], + [ "esp32:3", "sd2:DI", "green", [ "h0" ] ], + [ "esp32:8", "sd2:CS", "green", [ "h-19.2", "v-38.46" ] ], + [ "sd2:VCC", "esp32:3V3", "red", [ "h28.8", "v-57.46", "h67.2" ] ], + [ "sd2:GND", "esp32:GND.1", "black", [ "h38.4", "v0.11" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/diagram.esp32s3.json b/tests/validation/sdcard/diagram.esp32s3.json new file mode 100644 index 00000000000..14acb50500e --- /dev/null +++ b/tests/validation/sdcard/diagram.esp32s3.json @@ -0,0 +1,34 @@ +{ + "version": 1, + "author": "Jakub Andrýsek", + "editor": "wokwi", + "parts": [ + { "type": "board-esp32-s3-devkitc-1", "id": "esp", "top": 0, "left": 0, "attrs": {} }, + { "type": "wokwi-microsd-card", "id": "sd1", "top": 144.23, "left": -201.53, "attrs": {} }, + { + "type": "wokwi-microsd-card", + "id": "sd2", + "top": 18.77, + "left": 186.73, + "rotate": 180, + "attrs": {} + } + ], + "connections": [ + [ "esp:TX", "$serialMonitor:RX", "", [] ], + [ "esp:RX", "$serialMonitor:TX", "", [] ], + [ "sd1:CS", "esp:10", "green", [ "h57.6", "v-38.16" ] ], + [ "sd1:DI", "esp:11", "green", [ "h48", "v-19.11" ] ], + [ "sd1:SCK", "esp:12", "green", [ "h38.4", "v9.77" ] ], + [ "sd1:GND", "esp:GND.1", "black", [ "h9.6", "v57.71" ] ], + [ "sd1:DO", "esp:13", "green", [ "h67.2", "v38.69" ] ], + [ "sd1:VCC", "esp:3V3.2", "red", [ "h28.8", "v-143.86" ] ], + [ "sd2:GND", "esp:GND.3", "black", [ "h-19.2", "v86.47" ] ], + [ "sd2:VCC", "esp:3V3.2", "red", [ "h-19.2", "v-48.14", "h0", "v-86.4", "h-172.8", "v57.6" ] ], + [ "sd2:SCK", "esp:1", "green", [ "h-57.6", "v-38.39" ] ], + [ "sd2:DO", "esp:2", "green", [ "h-48", "v-9.71" ] ], + [ "sd2:DI", "esp:3", "green", [ "h-67.2", "v-47.91", "h-144", "v153.78" ] ], + [ "sd2:CS", "esp:8", "green", [ "h-57.6", "v-48.06", "h-144", "v153.78" ] ] + ], + "dependencies": {} +} diff --git a/tests/validation/sdcard/sdcard.ino b/tests/validation/sdcard/sdcard.ino new file mode 100644 index 00000000000..65cdf13d892 --- /dev/null +++ b/tests/validation/sdcard/sdcard.ino @@ -0,0 +1,543 @@ +#include "FS.h" +#include "SD.h" +#include "SPI.h" +#include // Needed for custom SD instance +#include +#include +#include +#include +#include + +#if defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) +#define SPI_COUNT_MAX 2 +#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) +#define SPI_COUNT_MAX 1 +#else +#define SPI_COUNT_MAX 4 // ESP32 +#endif + +#define MAX_FILES 5 + +class SPITestConfig { +public: + std::string name; + const char *mountpoint; + uint8_t spi_num; + int8_t sck; + int8_t miso; + int8_t mosi; + int8_t ss; + std::unique_ptr spi; + std::unique_ptr sd; + bool mounted = false; + + SPITestConfig(std::string name, const char *mountpoint, uint8_t spi_num, int8_t sck, int8_t miso, int8_t mosi, int8_t ss) + : name(name), mountpoint(mountpoint), spi_num(spi_num), sck(sck), miso(miso), mosi(mosi), ss(ss) { + Serial.printf("Creating SPITestConfig [%s] on SPI bus %d: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", name.c_str(), spi_num, sck, miso, mosi, ss); + } + + void begin(uint8_t max_files = MAX_FILES, bool format_if_empty = false) { + spi = std::make_unique(spi_num); + sd = std::make_unique(FSImplPtr(new VFSImpl())); + + TEST_ASSERT_TRUE_MESSAGE(spi->begin(sck, miso, mosi, ss), "Failed to begin SPI"); + TEST_ASSERT_TRUE_MESSAGE(sd->begin(ss, *spi, 4000000, mountpoint, max_files, format_if_empty), "Failed to mount SD card"); + mounted = true; + } + + void end() { + sd->end(); + mounted = false; + spi->end(); + spi.reset(); + sd.reset(); + Serial.printf("SPI and SD card for [%s] cleaned up\n", name.c_str()); + } +}; + +std::vector> spiTestConfigs; +using SpiTestFunction = std::function; + +void run_init_continuous(SpiTestFunction test_function, uint8_t max_files, bool format_if_empty = false) { + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + Serial.printf("Running test with SPI configuration: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", config.sck, config.miso, config.mosi, config.ss); + config.begin(max_files, format_if_empty); + test_function(config); + config.end(); + } +} + +void run_first_init(SpiTestFunction test_function, uint8_t max_files, bool format_if_empty = false) { + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.begin(max_files, format_if_empty); + } + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + test_function(config); + } + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.end(); + } +} + +void test_nonexistent_spi_interface(void) { + // Attempt to create a SPIClass with an invalid SPI number + SPIClass spiNotExist(SPI_COUNT_MAX); + TEST_ASSERT_FALSE_MESSAGE(spiNotExist.begin(), "SPIClass should not be initialized with an invalid SPI number"); + spiNotExist.end(); +} + +void run_multiple_ways(SpiTestFunction test_function, uint8_t max_files = MAX_FILES, bool format_if_empty = false) { + run_first_init(test_function, max_files, format_if_empty); + run_init_continuous(test_function, max_files, format_if_empty); +} + +void test_sd_basic(void) { + Serial.println("Running test_sd_basic"); + for (auto &ref : spiTestConfigs) { + SPITestConfig &config = *ref; + config.begin(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/"), "Root directory should exist"); + config.end(); + } +} + +void test_sd_dir(void) { + Serial.println("Running test_sd_dir"); + run_multiple_ways([](SPITestConfig &config) { + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir"), "Failed to create directory /testdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/testdir"), "Directory /testdir should exist after creation"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir"), "Failed to remove directory /testdir"); + TEST_ASSERT_FALSE_MESSAGE(config.sd->exists("/testdir"), "Directory /testdir should not exist after removal"); + }); +} + +void test_sd_file_operations(void) { + Serial.println("Running test_sd_file_operations"); + run_multiple_ways([](SPITestConfig &config) { + // Write a test file + File testFile = config.sd->open("/testfile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(testFile, "Failed to open file for writing"); + TEST_ASSERT_TRUE_MESSAGE(testFile.print("Hello, SD Card!"), "Failed to write to file"); + testFile.close(); + + // Read the test file + testFile = config.sd->open("/testfile.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(testFile, "Failed to open file for reading"); + String content = testFile.readString(); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Hello, SD Card!", content.c_str(), "File content does not match expected value"); + testFile.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testfile.txt"), "Failed to remove test file"); + }); +} + +void test_sd_open_limit(void) { + Serial.println("Running test_sd_open_limit"); + + run_init_continuous( + [](SPITestConfig &config) { + Serial.printf("Testing file open limit with SPI configuration: SCK=%d, MISO=%d, MOSI=%d, SS=%d\n", config.sck, config.miso, config.mosi, config.ss); + + // Open multiple files to test the limit + File file1 = config.sd->open("/file1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to open file1 for writing"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("File 1 content"), "Failed to write to file1"); + + File file2 = config.sd->open("/file2.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to open file2 for writing"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("File 2 content"), "Failed to write to file2"); + + // Attempt to open a third file, which should fail due to the limit + File file3 = config.sd->open("/file3.txt", FILE_WRITE); + TEST_ASSERT_FALSE_MESSAGE(file3, "Third file should not be opened due to max_files=2 limit"); + + // Clean up files + file1.close(); + file2.close(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file1.txt"), "Failed to remove file1"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file2.txt"), "Failed to remove file2"); + if (file3) { + file3.close(); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/file3.txt"), "Failed to remove file3"); + } + }, + 2 // max_files set to 2 for this test + ); +} + +void test_sd_directory_listing(void) { + Serial.println("Running test_sd_directory_listing"); + + run_multiple_ways([](SPITestConfig &config) { + // Create test directory structure + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir"), "Failed to create /testdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/testdir/subdir"), "Failed to create /testdir/subdir"); + + // Create test files + File file1 = config.sd->open("/testdir/file1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to create file1.txt"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("Content of file 1"), "Failed to write to file1.txt"); + file1.close(); + + File file2 = config.sd->open("/testdir/file2.dat", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to create file2.dat"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("Content of file 2"), "Failed to write to file2.dat"); + file2.close(); + + File subfile = config.sd->open("/testdir/subdir/subfile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(subfile, "Failed to create subfile.txt"); + TEST_ASSERT_TRUE_MESSAGE(subfile.print("Content of subfile"), "Failed to write to subfile.txt"); + subfile.close(); + + // Test directory listing + File dir = config.sd->open("/testdir"); + TEST_ASSERT_TRUE_MESSAGE(dir, "Failed to open /testdir for listing"); + TEST_ASSERT_TRUE_MESSAGE(dir.isDirectory(), "/testdir should be a directory"); + + int fileCount = 0; + int dirCount = 0; + File entry = dir.openNextFile(); + while (entry) { + if (entry.isDirectory()) { + dirCount++; + Serial.printf("Found directory: %s\n", entry.name()); + } else { + fileCount++; + Serial.printf("Found file: %s (size: %d bytes)\n", entry.name(), entry.size()); + } + entry.close(); + entry = dir.openNextFile(); + } + dir.close(); + + TEST_ASSERT_EQUAL_MESSAGE(2, fileCount, "Should find 2 files in /testdir"); + TEST_ASSERT_EQUAL_MESSAGE(1, dirCount, "Should find 1 subdirectory in /testdir"); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/subdir/subfile.txt"), "Failed to remove subfile.txt"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir/subdir"), "Failed to remove subdir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/file1.txt"), "Failed to remove file1.txt"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/testdir/file2.dat"), "Failed to remove file2.dat"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/testdir"), "Failed to remove testdir"); + }); +} + +void test_sd_file_size_operations(void) { + Serial.println("Running test_sd_file_size_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *testData = "This is a test file with specific content for size testing."; + size_t expectedSize = strlen(testData); + + // Create file with known content + File file = config.sd->open("/sizefile.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create sizefile.txt"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.print(testData), "Failed to write expected amount of data"); + file.close(); + + // Test file size + file = config.sd->open("/sizefile.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open sizefile.txt for reading"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.size(), "File size doesn't match expected size"); + + // Test available() method + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.available(), "Available bytes don't match file size"); + + // Test position tracking + TEST_ASSERT_EQUAL_MESSAGE(0, file.position(), "Initial position should be 0"); + + char buffer[20]; + file.readBytes(buffer, 10); + TEST_ASSERT_EQUAL_MESSAGE(10, file.position(), "Position should be 10 after reading 10 bytes"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize - 10, file.available(), "Available should decrease after reading"); + + // Test seek + TEST_ASSERT_TRUE_MESSAGE(file.seek(0), "Failed to seek to beginning"); + TEST_ASSERT_EQUAL_MESSAGE(0, file.position(), "Position should be 0 after seek"); + TEST_ASSERT_EQUAL_MESSAGE(expectedSize, file.available(), "Available should be full size after seek to start"); + + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/sizefile.txt"), "Failed to remove sizefile.txt"); + }); +} + +void test_sd_nested_directories(void) { + Serial.println("Running test_sd_nested_directories"); + + run_multiple_ways([](SPITestConfig &config) { + // Create nested directory structure + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1"), "Failed to create /level1"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1/level2"), "Failed to create /level1/level2"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/level1/level2/level3"), "Failed to create /level1/level2/level3"); + + // Create files at different levels + File file1 = config.sd->open("/level1/file_level1.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file1, "Failed to create file at level1"); + TEST_ASSERT_TRUE_MESSAGE(file1.print("Level 1 content"), "Failed to write to level1 file"); + file1.close(); + + File file2 = config.sd->open("/level1/level2/file_level2.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file2, "Failed to create file at level2"); + TEST_ASSERT_TRUE_MESSAGE(file2.print("Level 2 content"), "Failed to write to level2 file"); + file2.close(); + + File file3 = config.sd->open("/level1/level2/level3/file_level3.txt", FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file3, "Failed to create file at level3"); + TEST_ASSERT_TRUE_MESSAGE(file3.print("Level 3 content"), "Failed to write to level3 file"); + file3.close(); + + // Verify files exist at all levels + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/file_level1.txt"), "Level 1 file should exist"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/level2/file_level2.txt"), "Level 2 file should exist"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->exists("/level1/level2/level3/file_level3.txt"), "Level 3 file should exist"); + + // Test reading from nested files + File readFile = config.sd->open("/level1/level2/level3/file_level3.txt", FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(readFile, "Failed to open level3 file for reading"); + String content = readFile.readString(); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Level 3 content", content.c_str(), "Level 3 file content mismatch"); + readFile.close(); + + // Clean up from deepest to shallowest + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/level2/level3/file_level3.txt"), "Failed to remove level3 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1/level2/level3"), "Failed to remove level3 dir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/level2/file_level2.txt"), "Failed to remove level2 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1/level2"), "Failed to remove level2 dir"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove("/level1/file_level1.txt"), "Failed to remove level1 file"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/level1"), "Failed to remove level1 dir"); + }); +} + +void test_sd_file_count_in_directory(void) { + Serial.println("Running test_sd_file_count_in_directory"); + run_multiple_ways([](SPITestConfig &config) { + const char *fileBasePath = "/dir/a/b"; + const int numFiles = 5; + + auto getExpectedFile = [fileBasePath](int i) -> std::pair { + return {String(fileBasePath) + "/file" + String(i) + ".txt", "data:" + String(i)}; + }; + + { + // create nested directories + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/dir"), "mkdir /dir failed"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->mkdir("/dir/a"), "mkdir /dir/a failed"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->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 = config.sd->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 = config.sd->open(fileBasePath); + TEST_ASSERT_TRUE_MESSAGE(d && d.isDirectory(), "open(/dir/a/b) not a directory"); + + 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()); + } + } + + // Cleanup: remove files and directories in reverse order (deepest first) + for (int i = 0; i < numFiles; ++i) { + auto [filePath, _] = getExpectedFile(i); + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filePath.c_str()), ("Failed to remove file: " + filePath).c_str()); + } + // Remove directories + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir/a/b"), "Failed to remove directory: /dir/a/b"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir/a"), "Failed to remove directory: /dir/a"); + TEST_ASSERT_TRUE_MESSAGE(config.sd->rmdir("/dir"), "Failed to remove directory: /dir"); + }); +} + +void test_sd_file_append_operations(void) { + Serial.println("Running test_sd_file_append_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *filename = "/appendtest.txt"; + + // Create initial file + File file = config.sd->open(filename, FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create file for append test"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 1\n"), "Failed to write initial line"); + file.close(); + + // Append to file + file = config.sd->open(filename, FILE_APPEND); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open file for append"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 2\n"), "Failed to append second line"); + TEST_ASSERT_TRUE_MESSAGE(file.print("Line 3\n"), "Failed to append third line"); + file.close(); + + // Verify file contents + file = config.sd->open(filename, FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open file for reading"); + + String line1 = file.readStringUntil('\n'); + String line2 = file.readStringUntil('\n'); + String line3 = file.readStringUntil('\n'); + + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 1", line1.c_str(), "First line mismatch"); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 2", line2.c_str(), "Second line mismatch"); + TEST_ASSERT_EQUAL_STRING_MESSAGE("Line 3", line3.c_str(), "Third line mismatch"); + + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove append test file"); + }); +} + +void test_sd_large_file_operations(void) { + Serial.println("Running test_sd_large_file_operations"); + + run_multiple_ways([](SPITestConfig &config) { + const char *filename = "/largefile.bin"; + const size_t chunkSize = 512; + const size_t numChunks = 10; // 5KB total + const size_t totalSize = chunkSize * numChunks; + + // Create a buffer with known pattern + uint8_t writeBuffer[chunkSize]; + for (size_t i = 0; i < chunkSize; i++) { + writeBuffer[i] = (uint8_t)(i % 256); + } + + // Write large file in chunks + File file = config.sd->open(filename, FILE_WRITE); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to create large file"); + + for (size_t chunk = 0; chunk < numChunks; chunk++) { + size_t written = file.write(writeBuffer, chunkSize); + TEST_ASSERT_EQUAL_MESSAGE(chunkSize, written, "Failed to write complete chunk"); + } + file.close(); + + // Verify file size + file = config.sd->open(filename, FILE_READ); + TEST_ASSERT_TRUE_MESSAGE(file, "Failed to open large file for reading"); + TEST_ASSERT_EQUAL_MESSAGE(totalSize, file.size(), "Large file size mismatch"); + + // Read and verify chunks + uint8_t readBuffer[chunkSize]; + for (size_t chunk = 0; chunk < numChunks; chunk++) { + size_t bytesRead = file.readBytes((char *)readBuffer, chunkSize); + TEST_ASSERT_EQUAL_MESSAGE(chunkSize, bytesRead, "Failed to read complete chunk"); + + // Verify chunk content + for (size_t i = 0; i < chunkSize; i++) { + if (readBuffer[i] != writeBuffer[i]) { + char errorMsg[100]; + snprintf(errorMsg, sizeof(errorMsg), "Data mismatch at chunk %zu, byte %zu: expected %d, got %d", chunk, i, writeBuffer[i], readBuffer[i]); + TEST_FAIL_MESSAGE(errorMsg); + } + } + } + file.close(); + + // Clean up + TEST_ASSERT_TRUE_MESSAGE(config.sd->remove(filename), "Failed to remove large test file"); + }); +} + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(100); // Wait for Serial to be ready + } + Serial.println("SPI test START"); + +// pins for SD1 +#define SD1_SCK SCK +#define SD1_MISO MISO +#define SD1_MOSI MOSI +#define SD1_SS SS + +#if defined(CONFIG_IDF_TARGET_ESP32) +// pins for SD2 - ESP32 +#define SD2_SCK 25 +#define SD2_MISO 26 +#define SD2_MOSI 27 +#define SD2_SS 14 + +// ESP32 uses FSPI for the flash memory (for tests we use VSPI) +#undef FSPI +#define FSPI VSPI +#else +#define SD2_SCK 1 +#define SD2_MISO 2 +#define SD2_MOSI 3 +#define SD2_SS 8 +#endif + + spiTestConfigs.push_back(std::make_unique("FSPI", "/sd1", FSPI, SD1_SCK, SD1_MISO, SD1_MOSI, SD1_SS)); +#if SPI_COUNT_MAX >= 2 + spiTestConfigs.push_back(std::make_unique("HSPI", "/sd2", HSPI, SD2_SCK, SD2_MISO, SD2_MOSI, SD2_SS)); +#endif + + UNITY_BEGIN(); + RUN_TEST(test_nonexistent_spi_interface); + RUN_TEST(test_sd_basic); + RUN_TEST(test_sd_dir); + RUN_TEST(test_sd_file_operations); + RUN_TEST(test_sd_open_limit); + RUN_TEST(test_sd_directory_listing); + RUN_TEST(test_sd_file_size_operations); + RUN_TEST(test_sd_nested_directories); + RUN_TEST(test_sd_file_count_in_directory); + RUN_TEST(test_sd_file_append_operations); + RUN_TEST(test_sd_large_file_operations); + + UNITY_END(); + + Serial.println("SPI test END"); +} + +void loop() {} diff --git a/tests/validation/sdcard/test_sdcard.py b/tests/validation/sdcard/test_sdcard.py new file mode 100644 index 00000000000..dbd060fa5e9 --- /dev/null +++ b/tests/validation/sdcard/test_sdcard.py @@ -0,0 +1,2 @@ +def test_sdcard(dut): + dut.expect_unity_test_output(timeout=280) diff --git a/tests/validation/touch/ci.json b/tests/validation/touch/ci.json deleted file mode 100644 index 3ccb5dfabe8..00000000000 --- a/tests/validation/touch/ci.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "fqbn_append": "DebugLevel=verbose", - "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/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 54dd47ae9a9..00000000000 --- a/tests/validation/wifi/ci.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "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/bin_signing.exe b/tools/bin_signing.exe new file mode 100644 index 00000000000..524fd03a5ed Binary files /dev/null and b/tools/bin_signing.exe differ diff --git a/tools/bin_signing.py b/tools/bin_signing.py new file mode 100755 index 00000000000..1b020089b50 --- /dev/null +++ b/tools/bin_signing.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python3 +""" +OTA Update Signing Tool for ESP32 Arduino + +This script signs firmware binaries for secure OTA updates. +It supports both RSA and ECDSA signing schemes with various hash algorithms. + +Usage: + python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin + python bin_signing.py --generate-key rsa-2048 --out private_key.pem + python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem +""" + +import argparse +import sys +import os +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import rsa, ec, padding +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key + + +def generate_rsa_key(key_size, output_file): + """Generate an RSA private key""" + print(f"Generating RSA-{key_size} private key...") + private_key = rsa.generate_private_key(public_exponent=65537, key_size=key_size, backend=default_backend()) + + pem = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + + with open(output_file, "wb") as f: + f.write(pem) + + print(f"Private key saved to: {output_file}") + print("\nIMPORTANT: Keep this private key secure!") + print("Extract the public key with: python bin_signing.py --extract-pubkey", output_file) + + +def generate_ecdsa_key(curve_name, output_file): + """Generate an ECDSA private key""" + curves = { + "p256": ec.SECP256R1(), + "p384": ec.SECP384R1(), + } + + if curve_name not in curves: + print(f"Error: Unsupported curve. Supported curves: {', '.join(curves.keys())}") + sys.exit(1) + + print(f"Generating ECDSA-{curve_name.upper()} private key...") + private_key = ec.generate_private_key(curves[curve_name], backend=default_backend()) + + pem = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + + with open(output_file, "wb") as f: + f.write(pem) + + print(f"Private key saved to: {output_file}") + print("\nIMPORTANT: Keep this private key secure!") + print("Extract the public key with: python bin_signing.py --extract-pubkey", output_file) + + +def extract_public_key(private_key_file, output_file): + """Extract public key from private key""" + print(f"Extracting public key from {private_key_file}...") + + with open(private_key_file, "rb") as f: + private_key = load_pem_private_key(f.read(), password=None, backend=default_backend()) + + public_key = private_key.public_key() + + pem = public_key.public_bytes( + encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + with open(output_file, "wb") as f: + f.write(pem) + + print(f"Public key saved to: {output_file}") + + # Also generate a C header file for embedding in Arduino sketch + header_file = os.path.splitext(output_file)[0] + ".h" + with open(header_file, "w") as f: + f.write("// Public key for OTA signature verification\n") + f.write("// Include this in your Arduino sketch\n\n") + f.write("const uint8_t PUBLIC_KEY[] PROGMEM = {\n") + + # Add null terminator for mbedtls PEM parser + pem_bytes = pem + b"\x00" + for i in range(0, len(pem_bytes), 16): + chunk = pem_bytes[i : i + 16] + hex_str = ", ".join(f"0x{b:02x}" for b in chunk) + f.write(f" {hex_str},\n") + + f.write("};\n") + f.write(f"const size_t PUBLIC_KEY_LEN = {len(pem_bytes)};\n") + + print(f"C header file saved to: {header_file}") + + +def sign_binary(binary_file, key_file, output_file, hash_algo="sha256"): + """Sign a binary file""" + print(f"Signing {binary_file} with {key_file}...") + + # Read the binary + with open(binary_file, "rb") as f: + binary_data = f.read() + + print(f"Binary size: {len(binary_data)} bytes") + + # Load private key + with open(key_file, "rb") as f: + private_key = load_pem_private_key(f.read(), password=None, backend=default_backend()) + + # Select hash algorithm + hash_algos = { + "sha256": hashes.SHA256(), + "sha384": hashes.SHA384(), + "sha512": hashes.SHA512(), + } + + if hash_algo not in hash_algos: + print(f"Error: Unsupported hash algorithm. Supported: {', '.join(hash_algos.keys())}") + sys.exit(1) + + hash_obj = hash_algos[hash_algo] + + # Sign the binary + if isinstance(private_key, rsa.RSAPrivateKey): + print(f"Using RSA-PSS with {hash_algo.upper()}") + signature = private_key.sign( + binary_data, padding.PSS(mgf=padding.MGF1(hash_obj), salt_length=padding.PSS.MAX_LENGTH), hash_obj + ) + key_type = "RSA" + elif isinstance(private_key, ec.EllipticCurvePrivateKey): + print(f"Using ECDSA with {hash_algo.upper()}") + signature = private_key.sign(binary_data, ec.ECDSA(hash_obj)) + key_type = "ECDSA" + else: + print("Error: Unsupported key type") + sys.exit(1) + + print(f"Signature size: {len(signature)} bytes") + + # Pad signature to max size (512 bytes for RSA-4096) + max_sig_size = 512 + padded_signature = signature + b"\x00" * (max_sig_size - len(signature)) + + # Write signed binary (firmware + signature) + with open(output_file, "wb") as f: + f.write(binary_data) + f.write(padded_signature) + + signed_size = len(binary_data) + len(padded_signature) + print(f"Signed binary saved to: {output_file}") + print(f"Signed binary size: {signed_size} bytes (firmware: {len(binary_data)}, signature: {len(padded_signature)})") + print(f"\nKey type: {key_type}") + print(f"Hash algorithm: {hash_algo.upper()}") + + +def verify_signature(binary_file, pubkey_file, hash_algo="sha256"): + """Verify a signed binary""" + print(f"Verifying signature of {binary_file} with {pubkey_file}...") + + # Read the signed binary + with open(binary_file, "rb") as f: + signed_data = f.read() + + # The signature is the last 512 bytes (padded) + max_sig_size = 512 + if len(signed_data) < max_sig_size: + print("Error: File too small to contain signature") + sys.exit(1) + + binary_data = signed_data[:-max_sig_size] + signature = signed_data[-max_sig_size:] + + # Load public key + with open(pubkey_file, "rb") as f: + public_key = load_pem_public_key(f.read(), backend=default_backend()) + + # Select hash algorithm + hash_algos = { + "sha256": hashes.SHA256(), + "sha384": hashes.SHA384(), + "sha512": hashes.SHA512(), + } + + if hash_algo not in hash_algos: + print(f"Error: Unsupported hash algorithm. Supported: {', '.join(hash_algos.keys())}") + sys.exit(1) + + hash_obj = hash_algos[hash_algo] + + # Remove padding from signature + signature = signature.rstrip(b"\x00") + + # Verify the signature + try: + if isinstance(public_key, rsa.RSAPublicKey): + print(f"Verifying RSA-PSS signature with {hash_algo.upper()}") + public_key.verify( + signature, + binary_data, + padding.PSS(mgf=padding.MGF1(hash_obj), salt_length=padding.PSS.MAX_LENGTH), + hash_obj, + ) + elif isinstance(public_key, ec.EllipticCurvePublicKey): + print(f"Verifying ECDSA signature with {hash_algo.upper()}") + public_key.verify(signature, binary_data, ec.ECDSA(hash_obj)) + else: + print("Error: Unsupported key type") + sys.exit(1) + + print("✓ Signature verification SUCCESSFUL!") + return True + except Exception as e: + print(f"✗ Signature verification FAILED: {e}") + return False + + +def main(): + parser = argparse.ArgumentParser( + description="OTA Update Signing Tool for ESP32 Arduino", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + Generate RSA-2048 key: + python bin_signing.py --generate-key rsa-2048 --out private_key.pem + + Generate ECDSA-P256 key: + python bin_signing.py --generate-key ecdsa-p256 --out private_key.pem + + Extract public key: + python bin_signing.py --extract-pubkey private_key.pem --out public_key.pem + + Sign firmware: + python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin + + Sign with SHA-384: + python bin_signing.py --bin firmware.bin --key private_key.pem --out firmware_signed.bin --hash sha384 + + Verify signed firmware: + python bin_signing.py --verify firmware_signed.bin --pubkey public_key.pem + """, + ) + + parser.add_argument( + "--generate-key", + metavar="TYPE", + help="Generate a new key (rsa-2048, rsa-3072, rsa-4096, ecdsa-p256, ecdsa-p384)", + ) + parser.add_argument("--extract-pubkey", metavar="PRIVATE_KEY", help="Extract public key from private key") + parser.add_argument("--bin", metavar="FILE", help="Binary file to sign") + parser.add_argument("--key", metavar="FILE", help="Private key file (PEM format)") + parser.add_argument("--pubkey", metavar="FILE", help="Public key file for verification (PEM format)") + parser.add_argument("--out", metavar="FILE", help="Output file") + parser.add_argument( + "--hash", default="sha256", choices=["sha256", "sha384", "sha512"], help="Hash algorithm (default: sha256)" + ) + parser.add_argument("--verify", metavar="FILE", help="Verify a signed binary") + + args = parser.parse_args() + + if args.generate_key: + if not args.out: + print("Error: --out required for key generation") + sys.exit(1) + + key_type = args.generate_key.lower() + if key_type.startswith("rsa-"): + key_size = int(key_type.split("-")[1]) + generate_rsa_key(key_size, args.out) + elif key_type.startswith("ecdsa-"): + curve = key_type.split("-")[1] + generate_ecdsa_key(curve, args.out) + else: + print("Error: Invalid key type. Supported: rsa-2048, rsa-3072, rsa-4096, ecdsa-p256, ecdsa-p384") + sys.exit(1) + + elif args.extract_pubkey: + if not args.out: + print("Error: --out required for public key extraction") + sys.exit(1) + extract_public_key(args.extract_pubkey, args.out) + + elif args.verify: + if not args.pubkey: + print("Error: --pubkey required for verification") + sys.exit(1) + verify_signature(args.verify, args.pubkey, args.hash) + + elif args.bin and args.key: + if not args.out: + print("Error: --out required for signing") + sys.exit(1) + sign_binary(args.bin, args.key, args.out, args.hash) + + else: + parser.print_help() + sys.exit(1) + + +if __name__ == "__main__": + main() 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 index eaf6ba07b88..a9559940a68 100644 --- a/variants/XIAO_ESP32C5/pins_arduino.h +++ b/variants/XIAO_ESP32C5/pins_arduino.h @@ -39,4 +39,7 @@ 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_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/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_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_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/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_genesis_one/pins_arduino.h b/variants/axiometa_genesis_one/pins_arduino.h new file mode 100644 index 00000000000..279514ad2ea --- /dev/null +++ b/variants/axiometa_genesis_one/pins_arduino.h @@ -0,0 +1,106 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Battery monitoring (voltage divider /2) +#define VBAT_SENSE 10 + +// Battery voltage reading macro +#define getBatteryVoltage() ((analogRead(VBAT_SENSE) / 4095.0) * 3.3 * 2.0) + +// Fixed communication pins (shared across all ports) +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 47; +static const uint8_t SCL = 48; + +static const uint8_t SS = 1; +static const uint8_t MOSI = 11; +static const uint8_t MISO = 12; +static const uint8_t SCK = 13; + +// Port 1 IO pins +static const uint8_t P1_IO0 = 1; +static const uint8_t P1_IO1 = 14; +static const uint8_t P1_IO2 = 41; + +// Port 2 IO pins +static const uint8_t P2_IO0 = 2; +static const uint8_t P2_IO1 = 15; +static const uint8_t P2_IO2 = 42; + +// Port 3 IO pins +static const uint8_t P3_IO0 = 3; +static const uint8_t P3_IO1 = 16; +static const uint8_t P3_IO2 = 45; + +// Port 4 IO pins +static const uint8_t P4_IO0 = 4; +static const uint8_t P4_IO1 = 17; +static const uint8_t P4_IO2 = 46; + +// Port 5 IO pins +static const uint8_t P5_IO0 = 5; +static const uint8_t P5_IO1 = 18; +static const uint8_t P5_IO2 = 21; + +// Port 6 IO pins +static const uint8_t P6_IO0 = 6; +static const uint8_t P6_IO1 = 40; +static const uint8_t P6_IO2 = 38; + +// Port 7 IO pins +static const uint8_t P7_IO0 = 7; +static const uint8_t P7_IO1 = 9; +static const uint8_t P7_IO2 = 39; + +// Port 8 IO pins +static const uint8_t P8_IO0 = 48; +static const uint8_t P8_IO1 = 43; +static const uint8_t P8_IO2 = 44; + +// Analog capable pins (ESP32-S3 specific) +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; + +// Touch capable pins (ESP32-S3 specific) +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_round/pins_arduino.h b/variants/esp32p4_4ds_mipi_round/pins_arduino.h new file mode 100644 index 00000000000..c017db208ca --- /dev/null +++ b/variants/esp32p4_4ds_mipi_round/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 round 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/pins_arduino.h b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h index 491d75333a7..97e587ac0d0 100644 --- a/variants/fobe_quill_esp32s3_mesh/pins_arduino.h +++ b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h @@ -17,7 +17,8 @@ /* * Battery */ -#define PIN_VBAT (10) +#define PIN_VBAT (10) +#define BAT_VOLT_PIN PIN_VBAT /* * Buttons 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/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 8caf05582c9..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; 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 68897e6da05..781d6e6a0be 100644 --- a/variants/lolin_c3_pico/pins_arduino.h +++ b/variants/lolin_c3_pico/pins_arduino.h @@ -25,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_tab5/pins_arduino.h b/variants/m5stack_tab5/pins_arduino.h index df9c1fb0d65..9f91ef7a29d 100644 --- a/variants/m5stack_tab5/pins_arduino.h +++ b/variants/m5stack_tab5/pins_arduino.h @@ -49,6 +49,10 @@ 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 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/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/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/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/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 b77bc3966c1..edb93649228 100644 --- a/variants/watchy/pins_arduino.h +++ b/variants/watchy/pins_arduino.h @@ -29,7 +29,8 @@ 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 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; diff --git a/variants/waveshare_esp32_c3_zero/pins_arduino.h b/variants/waveshare_esp32_c3_zero/pins_arduino.h new file mode 100644 index 00000000000..97ff8294628 --- /dev/null +++ b/variants/waveshare_esp32_c3_zero/pins_arduino.h @@ -0,0 +1,48 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define PIN_RGB_LED 10 +// 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 = 21; +static const uint8_t RX = 20; + +static const uint8_t SDA = 8; +static const uint8_t SCL = 9; + +static const uint8_t SS = 7; +static const uint8_t MOSI = 6; +static const uint8_t MISO = 5; +static const uint8_t SCK = 4; + +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 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 = 6; +static const uint8_t D7 = 7; +static const uint8_t D8 = 8; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; +static const uint8_t D11 = 20; +static const uint8_t D12 = 21; + +#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;