diff --git a/.flake8 b/.flake8 index 5a2ed0b5b7f..eee32e982a8 100644 --- a/.flake8 +++ b/.flake8 @@ -5,6 +5,6 @@ doctests = True # W503 and W504 are mutually exclusive. PEP 8 recommends line break before. ignore = W503,E203 -max-complexity = 20 +max-complexity = 30 max-line-length = 120 select = E,W,F,C,N diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 64d241ba20a..d07ef88044d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,6 +11,7 @@ # CI /.github/ @lucasssvaz @me-no-dev @P-R-O-C-H-Y +/.github/codeql/ @lucasssvaz /.gitlab/ @lucasssvaz /tests/ @lucasssvaz @P-R-O-C-H-Y diff --git a/.github/ISSUE_TEMPLATE/Issue-report.yml b/.github/ISSUE_TEMPLATE/Issue-report.yml index 6dc1b0de171..97834925020 100644 --- a/.github/ISSUE_TEMPLATE/Issue-report.yml +++ b/.github/ISSUE_TEMPLATE/Issue-report.yml @@ -43,6 +43,7 @@ body: - latest stable Release (if not listed below) - latest development Release Candidate (RC-X) - latest master (checkout manually) + - v3.3.0 - v3.2.1 - v3.2.0 - v3.1.3 @@ -79,6 +80,17 @@ body: - other validations: required: true + - type: dropdown + id: type + attributes: + label: Type + description: How would you define the type of the issue? Please select from the types below. + options: + - "Task" + - "Bug" + - "Question" + validations: + required: true - type: input id: IDE attributes: diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index e879b09bec2..060397abd1e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,6 +3,3 @@ contact_links: - name: Arduino Core for Espressif Discord Server url: https://discord.gg/8xY6e9crwv about: Community Discord server for questions and help - - name: ESP32 Forum - Arduino - url: https://esp32.com/viewforum.php?f=19 - about: Official Forum for questions diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5e0d99c0457..6a803943690 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ ## Description of Change Please describe your proposed Pull Request and it's impact. -## Tests scenarios +## Test Scenarios Please describe on what Hardware and Software combinations you have tested this Pull Request and how. (*eg. I have tested my Pull Request on Arduino-esp32 core v2.0.2 with ESP32 and ESP32-S2 Board with this scenario*) diff --git a/.github/codeql/codeql-config.yml b/.github/codeql/codeql-config.yml new file mode 100644 index 00000000000..1c284c9d68e --- /dev/null +++ b/.github/codeql/codeql-config.yml @@ -0,0 +1,28 @@ +name: "CodeQL config" + +packs: + - trailofbits/cpp-queries + - githubsecuritylab/codeql-cpp-queries + - githubsecuritylab/codeql-python-queries + +queries: + - uses: security-extended + - uses: security-and-quality + +query-filters: + - exclude: + query path: + - /^experimental\/.*/ + - exclude: + tags contain: + - experimental + - exclude: + problem.severity: + - recommendation + - exclude: + id: tob/cpp/use-of-legacy-algorithm # We use legacy algorithms in many places for integrity checks + - exclude: + id: cpp/dead-code-goto # Too many false positives in no-build mode + +paths-ignore: + - tests/** diff --git a/.github/pytools/Sign-File.ps1 b/.github/pytools/Sign-File.ps1 index 09094096ac7..b45b7149ac6 100755 --- a/.github/pytools/Sign-File.ps1 +++ b/.github/pytools/Sign-File.ps1 @@ -19,7 +19,7 @@ function FindSignTool { if (Test-Path -Path $SignTool -PathType Leaf) { return $SignTool } - $sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0" + $sdkVers = "10.0.22000.0", "10.0.20348.0", "10.0.19041.0", "10.0.17763.0", "10.0.14393.0", "10.0.15063.0", "10.0.16299.0", "10.0.17134.0", "10.0.26100.0" Foreach ($ver in $sdkVers) { $SignTool = "${env:ProgramFiles(x86)}\Windows Kits\10\bin\${ver}\x64\signtool.exe" diff --git a/.github/scripts/get_affected.py b/.github/scripts/get_affected.py new file mode 100755 index 00000000000..85e35c40e43 --- /dev/null +++ b/.github/scripts/get_affected.py @@ -0,0 +1,959 @@ +""" +Affected Files Scanner +====================== + +Purpose +------- +Given a list of changed files, determine which Arduino sketches (.ino) or IDF component examples are affected +and should be rebuilt. + +Supports two modes: +- **Default mode**: Analyzes Arduino sketches (.ino files) +- **Component mode** (--component): Analyzes IDF component examples (directories) + +High-level workflow +------------------- +1) Discover include roots + - `include_folders` starts with `cores/esp32` and is extended with every `libraries/*/src` directory. + +2) Build forward dependency graph (`dependencies`) + - **Default mode**: Enumerate source files under `cores/esp32` and `libraries`. + - **Component mode**: Also includes files under `idf_component_examples`. + - For each file, parse `#include` lines and resolve headers: + a) First relative to the including file's directory. + b) Then under each folder in `include_folders`. + - Each `.ino` automatically depends on `cores/esp32/Arduino.h`. + - If a header with the same basename has an adjacent `.c`/`.cpp`, add those as dependencies as well. + +3) Optional: augment with Universal Ctags symbol mapping + - If Universal Ctags is available, the script runs it recursively on `include_folders` with JSON output. + - It indexes C/C++ function-like symbols (functions and prototypes), capturing scope (namespaces/classes) and signature. + - Two maps are produced: + - `ctags_header_to_qnames`: header path -> set of qualified names declared in that header. + - `ctags_defs_by_qname`: qualified name -> set of implementation source files (.c/.cpp/...). + - During dependency graph construction, when a header is included, the script finds all qualified names declared + in that header and adds their implementation files as dependencies of the includer. This connects declarations in + headers to implementations even when filenames do not match, and correctly handles namespaces/classes/methods via + qualified names. + +4) Build reverse dependency graph (`reverse_dependencies`) + - For each edge A -> B in `dependencies`, add B -> A in `reverse_dependencies`. + +5) Determine affected files + - **Default mode**: If any file in `build_files` changed, append preset sketches to affected list. + Start from changed files and walk `reverse_dependencies` breadth-first. Any `.ino` reached is marked as affected. + - **Component mode**: If any file in `idf_build_files` changed, all IDF component examples are affected. + If any file in `idf_project_files` changed, only that specific project is affected. + Walk dependencies to find component example directories containing affected files. + +6) Save artifacts + - `dependencies.json` (forward graph) + - `dependencies_reverse.json` (reverse graph) + - `ctags_tags.jsonl` (raw JSONL tag stream) + - `ctags_header_to_qnames.json` (header path -> set of qualified names declared in that header) + - `ctags_defs_by_qname.json` (qualified name -> set of implementation source files (.c/.cpp/...)) + +7) Set CI output + - If the script is running in a CI environment, set the following environment variables: + - `recompile_preset`: Whether the preset sketches should be recompiled + - `should_build`: Whether to build the sketches or component examples + - `chunks`: A list of chunk indices to build (maximum is defined by `MAX_CHUNKS`) + - `chunk_count`: The number of chunks to build (maximum is defined by `MAX_CHUNKS`) + +Build file patterns +-------------------- +- **build_files**: Core Arduino build system files (platform.txt, variants/**, etc.) +- **sketch_build_files**: Sketch-specific files (ci.json, *.csv in example directories) +- **idf_build_files**: Core IDF build system files (CMakeLists.txt, idf_component.yml, etc.) +- **idf_project_files**: Project-specific IDF files (per-example CMakeLists.txt, sdkconfig, etc.) + +Notes on accuracy and limitations +--------------------------------- +- Header-only or template-heavy code that defines functions inline in headers will not produce separate implementation + files. The header remains the dependency. +- If a declaration has no available definition, no implementation edge is added for it. +- Ctags does not require compilation but depends on static parsing. Exotic macro tricks may not be fully resolved. + +Usage +----- +python get_affected.py [changed_file2] ... [--component] [--debug] + +Options: + --component Analyze IDF component examples instead of Arduino sketches + --debug Enable debug messages and save debug artifacts to disk + +Output: +- **Default mode**: Prints affected sketches relative file paths to stdout +- **Component mode**: Prints affected IDF component examples relative directory paths to stdout +""" + +import sys +import argparse +from pathlib import Path +import os +import re +import queue +import json +import subprocess +import shutil +import fnmatch + +script_dir = Path(__file__).resolve().parent +project_root = (script_dir / "../../").resolve() + +is_ci = os.environ.get("CI", "false").lower() == "true" +is_pr = os.environ.get("IS_PR", "true").lower() == "true" +max_chunks = int(os.environ.get("MAX_CHUNKS", "15")) +chunk_count = 0 + +# Whether to analyze component examples instead of sketches. +component_mode = False + +# A list of files that are used by the CI and build system. +# If any of these files change, the preset sketches should be recompiled. +build_files = [ + "package.json", + "tools/get.py", + "tools/get.exe", + "platform.txt", + "programmers.txt", + ".github/workflows/push.yml", + ".github/scripts/on-push.sh", + ".github/scripts/sketch_utils.sh", + ".github/scripts/get_affected.py", + ".github/scripts/install-*", + "variants/esp32*/**", +] + +# Files that are used by the sketch build system. +# If any of these files change, the sketch should be recompiled. +sketch_build_files = [ + "libraries/*/examples/**/ci.json", + "libraries/*/examples/**/*.csv", +] + +# Files that are used by the IDF build system. +# If any of these files change, the IDF examples should be recompiled. +idf_build_files = [ + "CMakeLists.txt", + "idf_component.yml", + "Kconfig.projbuild", + ".github/workflows/build_component.yml", + ".github/scripts/on-push-idf.sh", + ".github/scripts/sketch_utils.sh", + ".github/scripts/get_affected.py", + ".github/scripts/check-cmakelists.sh", + "variants/esp32*/**", +] + +# Files that are used by the IDF examples. +# If any of these files change, the example that uses them should be recompiled. +idf_project_files = [ + "idf_component_examples/*/CMakeLists.txt", + "idf_component_examples/*/ci.json", + "idf_component_examples/*/*.csv", + "idf_component_examples/*/sdkconfig*", + "idf_component_examples/*/main/*", +] + +# Preset sketches that are used to test build system changes. +preset_sketch_files = [ + "libraries/NetworkClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino", + "libraries/BLE/examples/Server/Server.ino", + "libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino", + "libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino", +] + +# Whether to recompile the preset sketches for all platforms. +recompile_preset = 0 + +source_folders = ["cores/esp32", "libraries"] +include_folders = ["cores/esp32"] # This will later be extended to include all library folders. + +# A dictionary that maps a file to the files that it depends on. +dependencies = {} + +# A dictionary that maps a file to the files that depend on it (reverse dependencies). +reverse_dependencies = {} + +# A list of sketches that are affected by the changes. +affected_sketches = [] + +# A regular expression to match include statements in source files. +include_regex = re.compile(r'#include\s*[<"]([^">]+)[">]') + +# Ctags-based symbol index (optional) +ctags_executable = None +ctags_available = False +ctags_header_to_qnames: dict[str, set[str]] = {} +ctags_defs_by_qname: dict[str, set[str]] = {} +header_extensions = {".h", ".hpp", ".hh", ".hxx"} +source_extensions = {".c", ".cc", ".cpp", ".cxx", ".ino"} +skip_extensions = {".md"} + +def is_header_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in header_extensions + +def is_source_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in source_extensions + +def should_skip_file(path: str) -> bool: + return os.path.splitext(path)[1].lower() in skip_extensions + +def detect_universal_ctags() -> bool: + """ + Return True if Universal Ctags is available on PATH. + """ + exec_names = ["ctags-universal", "ctags"] + + global ctags_executable + for exec_name in exec_names: + ctags_path = shutil.which(exec_name) + if ctags_path: + break + if not ctags_path: + return False + + ctags_executable = ctags_path + + try: + out = subprocess.run([ctags_executable, "--version"], capture_output=True, text=True, check=False) + return "Universal Ctags" in (out.stdout + out.stderr) + except Exception: + return False + +def normalize_function_signature(signature: str) -> str: + """ + Normalize a function signature by removing parameter names, keeping only types. + + This handles cases where header declarations and implementations have different parameter names. + Uses a simple heuristic: the last word in each parameter is typically the parameter name. + + For example: + - "ltoa(long val, char *s, int radix)" -> "ltoa(long,char *,int)" + - "ltoa(long value, char *result, int base)" -> "ltoa(long,char *,int)" + + Args: + signature: The function signature string, e.g., "(long val, char *s, int radix)" + + Returns: + Normalized signature with parameter names removed, e.g., "(long,char *,int)" + """ + if not signature: + return signature + + # Normalize signatures: treat (void) and () as equivalent (both mean no parameters) + if signature == "(void)": + return "()" + + if not (signature.startswith("(") and signature.endswith(")")): + return signature + + # Handle const qualifier at the end (e.g., "(int i) const") + const_qualifier = "" + if signature.endswith(" const"): + signature = signature[:-6] # Remove " const" + const_qualifier = " const" + + # Extract parameter list without parentheses + param_list = signature[1:-1] + if not param_list.strip(): + return "()" + const_qualifier + + # Split by comma and process each parameter + params = [] + for param in param_list.split(","): + param = param.strip() + if not param: + continue + + # Handle default parameters (e.g., "int x = 5") + if "=" in param: + param = param.split("=")[0].strip() + + # Try simple approach first: remove the last word + # This works for most cases: "int x" -> "int", "MyStruct s" -> "MyStruct" + import re + + # Handle arrays first: "int arr[10]" -> "int [10]", "char *argv[]" -> "char *[]" + array_match = re.match(r'^(.+?)\s+(\w+)((?:\[\d*\])+)$', param) + if array_match: + type_part = array_match.group(1).strip() + array_brackets = array_match.group(3) + params.append(type_part + array_brackets) + else: + # Handle function pointers: "int (*func)(int, char)" -> "int (*)(int, char)" + func_ptr_match = re.match(r'^(.+?)\s*\(\s*\*\s*(\w+)\s*\)\s*\((.+?)\)\s*$', param) + if func_ptr_match: + return_type = func_ptr_match.group(1).strip() + inner_params = func_ptr_match.group(3).strip() + # Recursively normalize the inner parameters + if inner_params: + inner_normalized = normalize_function_signature(f"({inner_params})") + inner_normalized = inner_normalized[1:-1] # Remove outer parentheses + else: + inner_normalized = "" + params.append(f"{return_type} (*)({inner_normalized})") + else: + # Try simple approach: remove the last word + simple_match = re.match(r'^(.+)\s+(\w+)$', param) + if simple_match: + # Simple case worked - just remove the last word + type_part = simple_match.group(1).strip() + params.append(type_part) + else: + # Fallback to complex regex for edge cases with pointers + # First, try to match cases with pointers/references (including multiple *) + # Pattern: (everything before) (one or more * or &) (space) (parameter name) + m = re.match(r'^(.+?)(\s*[*&]+\s+)(\w+)$', param) + if m: + # Keep everything before the pointers, plus the pointers (without the space before param name) + type_part = m.group(1) + m.group(2).rstrip() + params.append(type_part.strip()) + else: + # Try to match cases without space between type and pointer: "char*ptr", "char**ptr" + m = re.match(r'^(.+?)([*&]+)(\w+)$', param) + if m: + # Keep everything before the pointers, plus the pointers + type_part = m.group(1) + m.group(2) + params.append(type_part.strip()) + else: + # Single word - assume it's a type + params.append(param.strip()) + + # Normalize spacing around pointers to ensure consistent output + # This ensures "char *" and "char*" both become "char *" + if params: + last_param = params[-1] + # Normalize spacing around * and & symbols + # Replace multiple spaces with single space, ensure space before * and & + normalized = re.sub(r'\s+', ' ', last_param) # Collapse multiple spaces + normalized = re.sub(r'\s*([*&]+)', r' \1', normalized) # Ensure space before * and & + normalized = re.sub(r'([*&]+)\s*', r'\1 ', normalized) # Ensure space after * and & + normalized = re.sub(r'\s+', ' ', normalized).strip() # Clean up extra spaces + params[-1] = normalized + + result = "(" + ",".join(params) + ")" + if const_qualifier: + result += const_qualifier + + return result + +def build_qname_from_tag(tag: dict) -> str: + """ + Compose a qualified name for a function/method using scope + name + signature. + Falls back gracefully if some fields are missing. + """ + scope = tag.get("scope") or "" + name = tag.get("name") or "" + signature = tag.get("signature") or "" + + # Normalize the signature to remove parameter names + signature = normalize_function_signature(signature) + + qparts = [] + if scope: + qparts.append(scope) + qparts.append(name) + qname = "::".join([p for p in qparts if p]) + return f"{qname}{signature}" + +def run_ctags_and_index(paths: list[str]) -> tuple[dict[str, set[str]], dict[str, set[str]], str]: + """ + Run Universal Ctags over given paths (relative to project_root) and build: + - header_to_qnames: header path -> set of qualified names declared in that header + - defs_by_qname: qualified name -> set of source files where it is defined + Paths in results are relative to project_root to match this script's expectations. + """ + cmd = [ + ctags_executable, "-R", "-f", "-", + "--map-C++=+.ino", + "--languages=C,C++", + "--kinds-C=+p", + "--kinds-C++=+pf", + "--fields=+nKSt+s", + "--output-format=json", + ] + paths + + try: + proc = subprocess.run( + cmd, + cwd=project_root, + capture_output=True, + text=True, + check=True, + ) + print(f"Ctags completed successfully. Output length: {len(proc.stdout)}", file=sys.stderr) + except subprocess.CalledProcessError as e: + print(f"Error: Ctags failed with return code {e.returncode}", file=sys.stderr) + print(f"Ctags stdout: {e.stdout}", file=sys.stderr) + print(f"Ctags stderr: {e.stderr}", file=sys.stderr) + return {}, {}, "" + except Exception as e: + print(f"Warning: Failed to run ctags: {e}", file=sys.stderr) + return {}, {}, "" + + header_to_qnames: dict[str, set[str]] = {} + defs_by_qname: dict[str, set[str]] = {} + + for line in proc.stdout.splitlines(): + line = line.strip() + if not line or not line.startswith("{"): + continue + try: + tag = json.loads(line) + except Exception: + continue + + path = tag.get("path") + if not path: + continue + + # Only consider C/C++ function-like symbols + kind = tag.get("kind") + # Accept both long names and single-letter kinds from ctags + is_function_like = kind in ("function", "prototype") or kind in ("f", "p") + if not is_function_like: + continue + + qname = build_qname_from_tag(tag) + if not qname: + continue + + if is_header_file(path): + header_to_qnames.setdefault(path, set()).add(qname) + elif is_source_file(path): + defs_by_qname.setdefault(qname, set()).add(path) + + print(f"Parsed {len(header_to_qnames)} headers and {len(defs_by_qname)} symbol definitions", file=sys.stderr) + return header_to_qnames, defs_by_qname, proc.stdout + +def save_ctags_artifacts(raw_jsonl: str, header_to_qnames: dict[str, set[str]], defs_by_qname: dict[str, set[str]]) -> None: + """ + Save ctags artifacts to disk: + - Raw JSONL tags stream (ctags_tags.jsonl) + - Header to qualified names map (ctags_header_to_qnames.json) + - Qualified name to implementation files map (ctags_defs_by_qname.json) + """ + try: + with open("ctags_tags.jsonl", "w", encoding="utf-8") as f: + f.write(raw_jsonl) + # Convert sets to sorted lists for JSON serialization + hdr_json = {k: sorted(list(v)) for k, v in header_to_qnames.items()} + defs_json = {k: sorted(list(v)) for k, v in defs_by_qname.items()} + with open("ctags_header_to_qnames.json", "w", encoding="utf-8") as f: + json.dump(hdr_json, f, indent=2, sort_keys=True) + with open("ctags_defs_by_qname.json", "w", encoding="utf-8") as f: + json.dump(defs_json, f, indent=2, sort_keys=True) + print("Ctags artifacts saved: ctags_tags.jsonl, ctags_header_to_qnames.json, ctags_defs_by_qname.json", file=sys.stderr) + except Exception as e: + print(f"Warning: Failed to save ctags artifacts: {e}", file=sys.stderr) + +def find_library_folders() -> list[str]: + """ + Find all library src folders and append them to include_folders. + """ + libraries_path = project_root / "libraries" + if libraries_path.exists(): + for lib_dir in libraries_path.iterdir(): + if lib_dir.is_dir(): + src_path = lib_dir / "src" + if src_path.is_dir(): + include_folders.append(str(src_path.relative_to(project_root))) + +def list_files() -> list[str]: + """ + List all source files in the project as relative paths to project_root. + Only search in specific folders to avoid processing the entire project. + """ + files = [] + for folder in source_folders: + folder_path = os.path.join(project_root, folder) + if os.path.exists(folder_path): + for root, dirs, file_list in os.walk(folder_path): + for file in file_list: + if is_source_file(file) or is_header_file(file): + abs_path = os.path.join(root, file) + rel_path = os.path.relpath(abs_path, project_root) + files.append(str(rel_path)) + return files + +def list_ino_files() -> list[str]: + """ + List all .ino files in the project. + """ + files = [] + for file in list_files(): + if file.endswith('.ino'): + files.append(file) + return files + +def list_idf_component_examples() -> list[str]: + """ + List all IDF component example directories in the project. + """ + examples = [] + examples_path = project_root / "idf_component_examples" + if examples_path.exists(): + for example_dir in examples_path.iterdir(): + if example_dir.is_dir(): + examples.append(str(example_dir.relative_to(project_root))) + return examples + +def search_file(filepath: str, folders: list[str]) -> str: + """ + Search the filepath using the folders as root folders. + If the file is found, return the full path, otherwise return None. + """ + for folder in folders: + full_path = os.path.join(folder, filepath) + if os.path.exists(full_path): + return full_path + return None + +def resolve_include_path(include_file: str, current_file: str) -> str: + """ + Resolve an include statement to a full file path. + """ + + # First try relative to the current file's directory + current_dir = os.path.dirname(current_file) + if current_dir: + relative_path = os.path.join(current_dir, include_file) + if os.path.exists(os.path.join(project_root, relative_path)): + return relative_path + + # Then try to find the file in the include folders + resolved_path = search_file(include_file, include_folders) + if resolved_path: + return os.path.relpath(resolved_path, project_root) + + return None + +def build_dependencies_graph() -> None: + """ + Build a dependency graph for the project. + """ + q = queue.Queue() + for file in list_ino_files(): + q.put(file) + for file in list_files(): + dependencies[file] = [] + + processed_files = set() + + while not q.empty(): + file = q.get() + if file in processed_files: + continue + + processed_files.add(file) + file_path = os.path.join(project_root, file) + + # Automatically add Arduino.h dependency for .ino files + if file.endswith('.ino'): + dependencies[file].append('cores/esp32/Arduino.h') + + try: + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: + content = f.read() + except Exception as e: + print(f"Warning: Could not read file {file}: {e}", file=sys.stderr) + continue + + for match in include_regex.finditer(content): + include_file = match.group(1) + if include_file.endswith('.h'): + header_path = resolve_include_path(include_file, file) + + # If the header file is found + if header_path: + # Add header file to dependencies + if header_path not in dependencies[file]: + dependencies[file].append(header_path) + # Only add to queue if it's a source file we haven't processed yet + if header_path in dependencies and header_path not in processed_files: + q.put(header_path) + + # Try to use ctags to match header declarations to implementation files + if ctags_available: + # Get qualified names of functions and methods declared in the header + qnames = ctags_header_to_qnames.get(header_path) + if qnames: + impl_files = set() + for qn in qnames: + # For each qualified name, get the implementation files + impl_files |= ctags_defs_by_qname.get(qn, set()) + for impl in impl_files: + # Skip .ino files - they should never be dependencies of other files + if impl.endswith('.ino'): + continue + # Add implementation file to dependencies + if impl not in dependencies[file]: + dependencies[file].append(impl) + if impl in dependencies and impl not in processed_files: + q.put(impl) + else: + # Fall back to resolving .cpp and .c files + cpp_path = resolve_include_path(include_file.replace('.h', '.cpp'), file) + c_path = resolve_include_path(include_file.replace('.h', '.c'), file) + + # Add C++ file to dependencies if it exists + if cpp_path and cpp_path not in dependencies[file]: + dependencies[file].append(cpp_path) + if cpp_path in dependencies and cpp_path not in processed_files: + q.put(cpp_path) + + # Add C file to dependencies if it exists + if c_path and c_path not in dependencies[file]: + dependencies[file].append(c_path) + if c_path in dependencies and c_path not in processed_files: + q.put(c_path) + + +def build_reverse_dependencies() -> None: + """ + Build a reverse dependency graph showing which files depend on each file. + """ + for file, deps in dependencies.items(): + for dependency in deps: + if dependency not in reverse_dependencies: + reverse_dependencies[dependency] = [] + reverse_dependencies[dependency].append(file) + +def has_build_files_changed(changed_files: list[str]) -> bool: + """ + Check if any of the build files have changed. + Supports glob patterns in build_files. + """ + for file in changed_files: + if should_skip_file(file): + continue + + if not component_mode: + for pattern in build_files: + if fnmatch.fnmatch(file, pattern): + return True + else: + for pattern in idf_build_files: + if fnmatch.fnmatch(file, pattern): + return True + + return False + +def has_sketch_build_files_changed(path: str) -> bool: + """ + Check if any of the sketch build files have changed. + """ + if should_skip_file(path): + return False + + if not component_mode: + for pattern in sketch_build_files: + if fnmatch.fnmatch(path, pattern): + return True + else: + for pattern in idf_project_files: + if fnmatch.fnmatch(path, pattern): + return True + return False + +def preprocess_changed_files(changed_files: list[str]) -> None: + """ + Preprocess the changed files to detect build system changes. + """ + # Special case: if the Arduino libs or tools changes, rebuild all sketches on PRs + if is_pr and not component_mode: + for changed in changed_files: + if changed == "package/package_esp32_index.template.json": + print("Package index changed - all sketches affected", file=sys.stderr) + all_sketches = list_ino_files() + for sketch in all_sketches: + if sketch not in affected_sketches: + affected_sketches.append(sketch) + # No need to continue preprocessing; dependency walk will still run afterwards + break + # If not a PR, skip preprocessing as we'll recompile everything anyway + if not is_pr: + return + + # Check if any build files have changed + if has_build_files_changed(changed_files): + if component_mode: + # If IDF build files changed, build all IDF component examples + print("IDF build files changed - all component examples affected", file=sys.stderr) + for example in list_idf_component_examples(): + if example not in affected_sketches: + affected_sketches.append(example) + else: + # If build files changed, build the preset sketches + print("Build files changed - preset sketches affected", file=sys.stderr) + global recompile_preset + recompile_preset = 1 + for file in preset_sketch_files: + if file not in affected_sketches: + affected_sketches.append(file) + + for file in changed_files: + if has_sketch_build_files_changed(file): + if component_mode: + # Extract the project name from idf_project_files pattern + # e.g., "idf_component_examples/hello_world/CMakeLists.txt" -> "idf_component_examples/hello_world" + if file.startswith("idf_component_examples/"): + parts = file.split("/") + if len(parts) >= 2: + project_path = "/".join(parts[:2]) # idf_component_examples/project_name + if project_path not in affected_sketches: + print(f"IDF project file changed - {project_path} affected", file=sys.stderr) + affected_sketches.append(project_path) + else: + dir_name = os.path.dirname(file) + sketch_name = dir_name + "/" + os.path.basename(dir_name) + ".ino" + if sketch_name not in affected_sketches: + affected_sketches.append(sketch_name) + +def find_affected_sketches(changed_files: list[str]) -> None: + """ + Find the sketches that are affected by the changes. + """ + + # If not a PR, recompile everything + if not is_pr: + if component_mode: + print("Not a PR - recompiling all IDF component examples", file=sys.stderr) + all_examples = list_idf_component_examples() + for example in all_examples: + if example not in affected_sketches: + affected_sketches.append(example) + print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr) + else: + print("Not a PR - recompiling all sketches", file=sys.stderr) + all_sketches = list_ino_files() + for sketch in all_sketches: + if sketch not in affected_sketches: + affected_sketches.append(sketch) + print(f"Total affected sketches: {len(affected_sketches)}", file=sys.stderr) + return + + preprocess_changed_files(changed_files) + + # Normal dependency-based analysis for non-critical changes + processed_files = set() + q = queue.Queue() + + if component_mode: + # Get all available component examples once for efficiency + all_examples = list_idf_component_examples() + else: + all_examples = [] + + for file in changed_files: + if not should_skip_file(file): + q.put(file) + + while not q.empty(): + file = q.get() + if file in processed_files: + continue + + processed_files.add(file) + + if component_mode: + # Check if this file belongs to an IDF component example + for example in all_examples: + if file.startswith(example + "/") and example not in affected_sketches: + affected_sketches.append(example) + else: + if file.endswith('.ino') and file not in affected_sketches: + affected_sketches.append(file) + + # Continue with reverse dependency traversal + if file in reverse_dependencies: + for dependency in reverse_dependencies[file]: + if dependency not in processed_files: + if component_mode: + # In component mode, only follow dependencies within the same example + # IDF component examples are isolated - no cross-example dependencies + current_example = None + dependency_example = None + + # Find which examples the current file and dependency belong to + for example in all_examples: + if file.startswith(example + "/"): + current_example = example + if dependency.startswith(example + "/"): + dependency_example = example + + # Traverse dependencies based on context: + # 1. If current file is NOT in any example (shared/core file), allow traversal to any example + # 2. If current file IS in an example, only traverse within the same example + should_traverse = False + + if not current_example: + # Current file is a shared/core file - can affect any example + should_traverse = True + elif current_example == dependency_example: + # Both files are in the same component example + should_traverse = True + + if should_traverse: + q.put(dependency) + if dependency_example and dependency_example not in affected_sketches: + affected_sketches.append(dependency_example) + else: + q.put(dependency) + if dependency.endswith('.ino') and dependency not in affected_sketches: + affected_sketches.append(dependency) + + if component_mode: + print(f"Total affected IDF component examples: {len(affected_sketches)}", file=sys.stderr) + if affected_sketches: + print("Affected IDF component examples:", file=sys.stderr) + for example in affected_sketches: + print(f" {example}", file=sys.stderr) + else: + print(f"Total affected sketches: {len(affected_sketches)}", file=sys.stderr) + if affected_sketches: + print("Affected sketches:", file=sys.stderr) + for sketch in affected_sketches: + print(f" {sketch}", file=sys.stderr) + +def save_dependencies_as_json(output_file: str = "dependencies.json") -> None: + """ + Save both the forward and reverse dependency graphs as JSON files. + """ + # Save forward dependencies + forward_deps_file = output_file + with open(forward_deps_file, 'w') as f: + json.dump(dependencies, f, indent=2, sort_keys=True) + print(f"Forward dependencies saved to: {forward_deps_file}", file=sys.stderr) + + # Save reverse dependencies + reverse_deps_file = output_file.replace('.json', '_reverse.json') + with open(reverse_deps_file, 'w') as f: + json.dump(reverse_dependencies, f, indent=2, sort_keys=True) + print(f"Reverse dependencies saved to: {reverse_deps_file}", file=sys.stderr) + +def check_preset_files_affected(): + """ + Check if any of the preset sketch files are in the affected sketches list. + If so, set recompile_preset to True. + """ + global recompile_preset + + # If not a PR, always recompile preset sketches + if not is_pr: + if not component_mode: # Only check preset files in sketch mode + print("Not a PR - setting recompile_preset=1 for all preset sketches", file=sys.stderr) + recompile_preset = 1 + return + + if not component_mode: # Only check preset files in sketch mode + for preset_file in preset_sketch_files: + if preset_file in affected_sketches: + print(f"Preset sketches affected - setting recompile_preset=1", file=sys.stderr) + recompile_preset = 1 + break + +def set_ci_output(print_vars: bool=True): + """ + Set GITHUB_OUTPUT environment variable + """ + # Determine output target + if not is_ci and print_vars: + print("Not a CI environment - printing variables to stdout:", file=sys.stderr) + f = sys.stderr + elif is_ci: + out = os.environ["GITHUB_OUTPUT"] + f = open(out, "a") + else: + return + + try: + # Write all output values + f.write(f"recompile_preset={recompile_preset}\n") + chunk_count = len(affected_sketches) + if chunk_count > 0: + f.write(f"should_build=1\n") + if chunk_count > max_chunks: + print(f"More sketches than the allowed number of chunks found. Limiting to {max_chunks} chunks.", file=sys.stderr) + chunk_count = max_chunks + chunks='["0"' + for i in range(1, chunk_count): + chunks+=f",\"{i}\"" + chunks+="]" + f.write(f"chunks={chunks}\n") + else: + f.write(f"should_build=0\n") + f.write(f"chunk_count={chunk_count}\n") + finally: + # Close file if we opened it (CI mode) + if is_ci: + f.close() + +def main(): + parser = argparse.ArgumentParser(description="Affected Sketches Scanner") + parser.add_argument("changed_files", nargs="+", help="List of changed files (e.g., file1.cpp file2.h file3.cpp)") + parser.add_argument("--component", action="store_true", help="Get affected component examples instead of sketches") + parser.add_argument("--debug", action="store_true", help="Enable debug messages and save debug artifacts to disk") + args = parser.parse_args() + + changed_files = args.changed_files + global component_mode + component_mode = args.component + if component_mode: + print(f"Analyzing IDF component examples...", file=sys.stderr) + source_folders.append("idf_component_examples") + else: + print(f"Analyzing sketches...", file=sys.stderr) + + print(f"Finding include folders...", file=sys.stderr) + find_library_folders() + print(f"Found {len(include_folders)} include folders", file=sys.stderr) + + # Initialize ctags-based symbol index if available + global ctags_available, ctags_header_to_qnames, ctags_defs_by_qname + ctags_available = detect_universal_ctags() + if ctags_available: + print("Universal Ctags detected - indexing symbols for header-to-implementation mapping...", file=sys.stderr) + try: + hdr_to_qn, defs_by_qn, raw_jsonl = run_ctags_and_index(source_folders) + ctags_header_to_qnames, ctags_defs_by_qname = hdr_to_qn, defs_by_qn + print(f"Ctags index built: {len(ctags_header_to_qnames)} headers, {len(ctags_defs_by_qname)} symbols", file=sys.stderr) + if args.debug: + print("Saving ctags artifacts...", file=sys.stderr) + save_ctags_artifacts(raw_jsonl, ctags_header_to_qnames, ctags_defs_by_qname) + except Exception as e: + print(f"Warning: Ctags indexing failed: {e}", file=sys.stderr) + ctags_available = False + else: + print("Universal Ctags not found - proceeding without symbol-based mapping", file=sys.stderr) + + print(f"Building dependency graph...", file=sys.stderr) + build_dependencies_graph() + print(f"Processed {len(dependencies)} files", file=sys.stderr) + + print(f"Building reverse dependency graph...", file=sys.stderr) + build_reverse_dependencies() + + # Save dependencies as JSON artifacts + if args.debug: + print(f"Saving dependencies as JSON artifacts...", file=sys.stderr) + save_dependencies_as_json() + + print(f"Finding affected sketches...", file=sys.stderr) + if not is_pr: + print("Not a PR - will recompile everything", file=sys.stderr) + find_affected_sketches(changed_files) + + print(f"Checking if preset sketch files were affected...", file=sys.stderr) + check_preset_files_affected() + + print(f"Printing affected sketches...", file=sys.stderr) + for sketch in affected_sketches: + print(sketch) + + set_ci_output(args.debug) + +if __name__ == "__main__": + main() diff --git a/.github/scripts/merge_packages.py b/.github/scripts/merge_packages.py index 8d1f200ec5c..3ca781678e1 100755 --- a/.github/scripts/merge_packages.py +++ b/.github/scripts/merge_packages.py @@ -17,7 +17,8 @@ def load_package(filename): - pkg = json.load(open(filename))["packages"][0] + with open(filename) as f: + pkg = json.load(f)["packages"][0] print("Loaded package {0} from {1}".format(pkg["name"], filename), file=sys.stderr) print("{0} platform(s), {1} tools".format(len(pkg["platforms"]), len(pkg["tools"])), file=sys.stderr) return pkg diff --git a/.github/scripts/on-push-idf.sh b/.github/scripts/on-push-idf.sh index 72e7c7f574e..166bfe13eb1 100644 --- a/.github/scripts/on-push-idf.sh +++ b/.github/scripts/on-push-idf.sh @@ -7,27 +7,33 @@ CHECK_REQUIREMENTS="./components/arduino-esp32/.github/scripts/sketch_utils.sh c # Export IDF environment . ${IDF_PATH}/export.sh -# Find all examples in ./components/arduino-esp32/idf_component_examples -idf_component_examples=$(find ./components/arduino-esp32/idf_component_examples -mindepth 1 -maxdepth 1 -type d) - -for example in $idf_component_examples; do - if [ -f "$example"/ci.json ]; then +if [ -n "$1" ]; then + # If a file is provided, use it to get the affected examples. + affected_examples=$(cat "$1" | sort) +else + # Otherwise, find all examples in ./components/arduino-esp32/idf_component_examples + affected_examples=$(find ./components/arduino-esp32/idf_component_examples -mindepth 1 -maxdepth 1 -type d | sed 's|^\./components/arduino-esp32/||' | sort) +fi + +for example in $affected_examples; do + example_path="$PWD/components/arduino-esp32/$example" + if [ -f "$example_path/ci.json" ]; then # If the target is listed as false, skip the sketch. Otherwise, include it. - is_target=$(jq -r --arg target "$IDF_TARGET" '.targets[$target]' "$example"/ci.json) + is_target=$(jq -r --arg target "$IDF_TARGET" '.targets[$target]' "$example_path/ci.json") if [[ "$is_target" == "false" ]]; then printf "\n\033[93mSkipping %s for target %s\033[0m\n\n" "$example" "$IDF_TARGET" continue fi fi - idf.py -C "$example" set-target "$IDF_TARGET" + idf.py --preview -C "$example_path" set-target "$IDF_TARGET" - has_requirements=$(${CHECK_REQUIREMENTS} "$example" "$example/sdkconfig") + has_requirements=$(${CHECK_REQUIREMENTS} "$example_path" "$example_path/sdkconfig") if [ "$has_requirements" -eq 0 ]; then printf "\n\033[93m%s does not meet the requirements for %s. Skipping...\033[0m\n\n" "$example" "$IDF_TARGET" continue fi printf "\n\033[95mBuilding %s\033[0m\n\n" "$example" - idf.py -C "$example" -DEXTRA_COMPONENT_DIRS="$PWD/components" build + idf.py --preview -C "$example_path" -DEXTRA_COMPONENT_DIRS="$PWD/components" build done diff --git a/.github/scripts/process_sarif.py b/.github/scripts/process_sarif.py new file mode 100644 index 00000000000..fad689b6390 --- /dev/null +++ b/.github/scripts/process_sarif.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 + +# This script is used to process the SARIF file generated by CodeQL and +# to rename files back to .ino and adjust line numbers to match the original .ino files. + +import json +import sys +import os + +def process_artifact_location(artifact_location, renamed_files): + """ + Process a single artifact location to rename .cpp files back to .ino + """ + if 'uri' in artifact_location: + uri = artifact_location['uri'] + if uri in renamed_files: + print(f"Renaming file: {uri} -> {renamed_files[uri]}") + artifact_location['uri'] = renamed_files[uri] + return True + return False + +def process_region(region): + """ + Adjust line numbers in a region by decreasing them by 1 + """ + if 'startLine' in region: + region['startLine'] = max(1, region['startLine'] - 1) + if 'endLine' in region: + region['endLine'] = max(1, region['endLine'] - 1) + +def process_physical_location(physical_location, renamed_files): + """ + Process a physical location to rename files and adjust line numbers + """ + file_renamed = False + + if 'artifactLocation' in physical_location: + if process_artifact_location(physical_location['artifactLocation'], renamed_files): + file_renamed = True + + # Adjust line numbers if the file was renamed + if file_renamed and 'region' in physical_location: + process_region(physical_location['region']) + + return file_renamed + + +def process_sarif_file(sarif_file, renamed_files_file): + """ + Process SARIF file to rename files back to .ino and adjust line numbers + """ + # Read the renamed files mapping + with open(renamed_files_file, 'r') as f: + renamed_files = json.load(f) + + print(f"Loaded {len(renamed_files)} file mappings:") + for cpp_file, ino_file in renamed_files.items(): + print(f" {cpp_file} -> {ino_file}") + + + # Read the SARIF file + with open(sarif_file, 'r') as f: + sarif_data = json.load(f) + + files_processed = 0 + + # Process each run + if 'runs' in sarif_data: + for run in sarif_data['runs']: + # Process results + if 'results' in run: + for result in run['results']: + # Process all locations in the result + if 'locations' in result: + for location in result['locations']: + if 'physicalLocation' in location: + if process_physical_location(location['physicalLocation'], renamed_files): + files_processed += 1 + + # Process related locations if they exist + if 'relatedLocations' in result: + for location in result['relatedLocations']: + if 'physicalLocation' in location: + if process_physical_location(location['physicalLocation'], renamed_files): + files_processed += 1 + + # Process artifacts if they exist + if 'artifacts' in run: + for artifact in run['artifacts']: + if 'location' in artifact and 'uri' in artifact['location']: + uri = artifact['location']['uri'] + if uri in renamed_files: + artifact['location']['uri'] = renamed_files[uri] + files_processed += 1 + + print(f"Processed {files_processed} file references") + + # Write the processed SARIF file + with open(sarif_file, 'w') as f: + json.dump(sarif_data, f, indent=2) + +def main(): + if len(sys.argv) != 3: + print("Usage: python3 sarif_nobuild.py ") + sys.exit(1) + + sarif_file = sys.argv[1] + renamed_files_file = sys.argv[2] + + # Check if files exist + if not os.path.exists(sarif_file): + print(f"SARIF file not found: {sarif_file}") + sys.exit(1) + + if not os.path.exists(renamed_files_file): + print(f"Renamed files mapping not found: {renamed_files_file}") + sys.exit(1) + + try: + process_sarif_file(sarif_file, renamed_files_file) + print("SARIF file processed successfully") + except Exception as e: + print(f"Error processing SARIF file: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/.github/scripts/set_push_chunks.sh b/.github/scripts/set_push_chunks.sh deleted file mode 100644 index ff0af7da6e8..00000000000 --- a/.github/scripts/set_push_chunks.sh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/bash - -build_all=false -chunks_count=0 - -if [[ $CORE_CHANGED == 'true' ]] || [[ $IS_PR != 'true' ]]; then - echo "Core files changed or not a PR. Building all." - build_all=true - chunks_count=$MAX_CHUNKS -elif [[ $LIB_CHANGED == 'true' ]]; then - echo "Libraries changed. Building only affected sketches." - if [[ $NETWORKING_CHANGED == 'true' ]]; then - echo "Networking libraries changed. Building networking related sketches." - networking_sketches="$(find libraries/WiFi -name '*.ino') " - networking_sketches+="$(find libraries/Ethernet -name '*.ino') " - networking_sketches+="$(find libraries/PPP -name '*.ino') " - networking_sketches+="$(find libraries/NetworkClientSecure -name '*.ino') " - networking_sketches+="$(find libraries/WebServer -name '*.ino') " - fi - if [[ $FS_CHANGED == 'true' ]]; then - echo "FS libraries changed. Building FS related sketches." - fs_sketches="$(find libraries/SD -name '*.ino') " - fs_sketches+="$(find libraries/SD_MMC -name '*.ino') " - fs_sketches+="$(find libraries/SPIFFS -name '*.ino') " - fs_sketches+="$(find libraries/LittleFS -name '*.ino') " - fs_sketches+="$(find libraries/FFat -name '*.ino') " - fi - sketches="$networking_sketches $fs_sketches" - for file in $LIB_FILES; do - lib=$(echo "$file" | awk -F "/" '{print $1"/"$2}') - if [[ "$file" == *.ino ]]; then - # If file ends with .ino, add it to the list of sketches - echo "Sketch found: $file" - sketches+="$file " - elif [[ "$file" == "$lib/src/"* ]]; then - # If file is inside the src directory, find all sketches in the lib/examples directory - echo "Library src file found: $file" - if [[ -d $lib/examples ]]; then - lib_sketches=$(find "$lib"/examples -name '*.ino') - sketches+="$lib_sketches " - echo "Library sketches: $lib_sketches" - fi - else - # If file is in a example folder but it is not a sketch, find all sketches in the current directory - echo "File in example folder found: $file" - sketch=$(find "$(dirname "$file")" -name '*.ino') - sketches+="$sketch " - echo "Sketch in example folder: $sketch" - fi - echo "" - done -fi - -if [[ -n $sketches ]]; then - # Remove duplicates - sketches=$(echo "$sketches" | tr ' ' '\n' | sort | uniq) - for sketch in $sketches; do - echo "$sketch" >> sketches_found.txt - chunks_count=$((chunks_count+1)) - done - echo "Number of sketches found: $chunks_count" - echo "Sketches:" - echo "$sketches" - - if [[ $chunks_count -gt $MAX_CHUNKS ]]; then - echo "More sketches than the allowed number of chunks found. Limiting to $MAX_CHUNKS chunks." - chunks_count=$MAX_CHUNKS - fi -fi - -chunks='["0"' -for i in $(seq 1 $(( chunks_count - 1 )) ); do - chunks+=",\"$i\"" -done -chunks+="]" - -{ - echo "build_all=$build_all" - echo "build_libraries=$BUILD_LIBRARIES" - echo "build_static_sketches=$BUILD_STATIC_SKETCHES" - echo "build_idf=$BUILD_IDF" - echo "chunk_count=$chunks_count" - echo "chunks=$chunks" -} >> "$GITHUB_OUTPUT" diff --git a/.github/scripts/sketch_utils.sh b/.github/scripts/sketch_utils.sh index 01ceafe9af1..02990ca8914 100755 --- a/.github/scripts/sketch_utils.sh +++ b/.github/scripts/sketch_utils.sh @@ -244,6 +244,14 @@ function build_sketch { # build_sketch [ext fi fi + # Install libraries from ci.json if they exist + install_libs -ai "$ide_path" -s "$sketchdir" + install_result=$? + if [ $install_result -ne 0 ]; then + echo "ERROR: Library installation failed for $sketchname" >&2 + exit $install_result + fi + ARDUINO_CACHE_DIR="$HOME/.arduino/cache.tmp" if [ -n "$ARDUINO_BUILD_DIR" ]; then build_dir="$ARDUINO_BUILD_DIR" @@ -349,7 +357,7 @@ function build_sketch { # build_sketch [ext unset options } -function count_sketches { # count_sketches [target] [file] [ignore-requirements] +function count_sketches { # count_sketches [target] [ignore-requirements] [file] local path=$1 local target=$2 local ignore_requirements=$3 @@ -368,7 +376,7 @@ function count_sketches { # count_sketches [target] [file] [ignore-requir fi if [ -f "$file" ]; then - sketches=$(cat "$file") + sketches=$(cat "$file" | sort) else sketches=$(find "$path" -name '*.ino' | sort) fi @@ -580,6 +588,160 @@ function build_sketches { # build_sketches &2 || printf '%s\n' "$out" >&2 + fi +} + +function install_libs { # install_libs [-v] + local ide_path="" + local sketchdir="" + local verbose=false + + while [ -n "$1" ]; do + case "$1" in + -ai ) shift; ide_path=$1 ;; + -s ) shift; sketchdir=$1 ;; + -v ) verbose=true ;; + * ) + echo "ERROR: Unknown argument: $1" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + ;; + esac + shift + done + + if [ -z "$ide_path" ]; then + echo "ERROR: IDE path not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ -z "$sketchdir" ]; then + echo "ERROR: Sketch directory not provided" >&2 + echo "USAGE: install_libs -ai -s [-v]" >&2 + return 1 + fi + if [ ! -f "$ide_path/arduino-cli" ]; then + echo "ERROR: arduino-cli not found at $ide_path/arduino-cli" >&2 + return 1 + fi + + if [ ! -f "$sketchdir/ci.json" ]; then + [ "$verbose" = true ] && echo "No ci.json 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 + 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" + return 0 + elif [ "$libs_type" != "array" ]; then + echo "ERROR: libs field in ci.json 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) + if [ "$libs_count" -eq 0 ]; then + [ "$verbose" = true ] && echo "libs array is empty in ci.json, skipping library installation" + return 0 + fi + + echo "Installing $libs_count libraries from $sketchdir/ci.json" + + local needs_unsafe=false + local original_unsafe_setting="" + local libs + libs=$(jq -r '.libs[]? // empty' "$sketchdir/ci.json") + + # Detect any git-like URL (GitHub/GitLab/Bitbucket/self-hosted/ssh) + for lib in $libs; do + if is_git_like_url "$lib"; then + needs_unsafe=true + break + fi + done + + if [ "$needs_unsafe" = true ]; then + [ "$verbose" = true ] && echo "Checking current unsafe install setting..." + original_unsafe_setting=$("$ide_path/arduino-cli" config get library.enable_unsafe_install 2>/dev/null || echo "false") + if [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Enabling unsafe installs for Git URLs..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install true >/dev/null 2>&1 || \ + echo "WARNING: Failed to enable unsafe installs, Git URL installs may fail" >&2 + else + [ "$verbose" = true ] && echo "Unsafe installs already enabled" + fi + fi + + local rc=0 install_status=0 output="" + for lib in $libs; do + [ "$verbose" = true ] && echo "Processing library: $lib" + + if is_git_like_url "$lib"; then + [ "$verbose" = true ] && echo "Installing library from git URL: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install --git-url "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install --git-url "$lib" 2>&1) + install_status=$? + fi + else + [ "$verbose" = true ] && echo "Installing library by name: $lib" + if [ "$verbose" = true ]; then + "$ide_path/arduino-cli" lib install "$lib" + install_status=$? + else + output=$("$ide_path/arduino-cli" lib install "$lib" 2>&1) + install_status=$? + fi + fi + + # Treat "already installed"/"up to date" as success (idempotent) + if [ $install_status -ne 0 ] && echo "$output" | grep -qiE 'already installed|up to date'; then + install_status=0 + fi + + if [ "$verbose" != true ]; then + print_err_warnings "$install_status" "$output" + fi + + if [ $install_status -ne 0 ]; then + echo "ERROR: Failed to install library: $lib" >&2 + rc=$install_status + break + else + [ "$verbose" = true ] && echo "Successfully installed library: $lib" + fi + done + + if [ "$needs_unsafe" = true ] && [ "$original_unsafe_setting" = "false" ]; then + [ "$verbose" = true ] && echo "Restoring original unsafe install setting..." + "$ide_path/arduino-cli" config set library.enable_unsafe_install false >/dev/null 2>&1 || true + fi + + [ $rc -eq 0 ] && echo "Library installation completed" + return $rc +} + + USAGE=" USAGE: ${0} [command] [options] Available commands: @@ -587,6 +749,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. " cmd=$1 @@ -606,6 +769,8 @@ case "$cmd" in ;; "check_requirements") check_requirements "$@" ;; + "install_libs") install_libs "$@" + ;; *) echo "ERROR: Unrecognized command" echo "$USAGE" diff --git a/.github/scripts/tests_run.sh b/.github/scripts/tests_run.sh index 1c4bee79742..c85db2e984a 100755 --- a/.github/scripts/tests_run.sh +++ b/.github/scripts/tests_run.sh @@ -115,14 +115,14 @@ function run_test { rm "$sketchdir"/diagram.json 2>/dev/null || true result=0 - printf "\033[95mpytest \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" - bash -c "set +e; pytest \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q}; exit \$?" || result=$? + printf "\033[95mpytest -s \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\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 "\n" if [ $result -ne 0 ]; then result=0 printf "\033[95mRetrying test: %s -- Config: %s\033[0m\n" "$sketchname" "$i" - printf "\033[95mpytest \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\033[0m\n" "$sketchdir" "$sketchname" "$build_dir" "$report_file" "$test_type" "$platform" "$target" "$sketchname" "$i" "${extra_args[*]@Q}" - bash -c "set +e; pytest \"$sketchdir/test_$sketchname.py\" --build-dir \"$build_dir\" --junit-xml=\"$report_file\" -o junit_suite_name=${test_type}_${platform}_${target}_${sketchname}${i} ${extra_args[*]@Q}; exit \$?" || result=$? + printf "\033[95mpytest -s \"%s/test_%s.py\" --build-dir \"%s\" --junit-xml=\"%s\" -o junit_suite_name=%s_%s_%s_%s%s %s\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 "\n" if [ $result -ne 0 ]; then printf "\033[91mFailed test: %s -- Config: %s\033[0m\n\n" "$sketchname" "$i" diff --git a/.github/scripts/update_esptool.py b/.github/scripts/update_esptool.py old mode 100644 new mode 100755 index d99462fcb8f..0757e8085ea --- a/.github/scripts/update_esptool.py +++ b/.github/scripts/update_esptool.py @@ -33,6 +33,7 @@ import os import shutil import stat +import subprocess import tarfile import zipfile import hashlib @@ -201,10 +202,34 @@ def get_release_info(version): response.raise_for_status() return response.json() +def create_branch_and_commit(version, json_path): + """Create a new branch and commit the changes to it.""" + branch_name = f"update-esptool-{version}" + commit_message = f"change(esptool): Upgrade to version {version}" + + try: + # Create and checkout new branch + subprocess.run(["git", "checkout", "-b", branch_name], check=True, capture_output=True, text=True) + print(f"Created and switched to new branch: {branch_name}") + + # Stage the JSON file + subprocess.run(["git", "add", str(json_path)], check=True, capture_output=True, text=True) + print(f"Staged file: {json_path}") + + # Commit the changes + subprocess.run(["git", "commit", "-m", commit_message], check=True, capture_output=True, text=True) + print(f"Committed changes with message: {commit_message}") + + except subprocess.CalledProcessError as e: + print(f"Git operation failed: {e}") + print(f"Command output: {e.stderr if e.stderr else e.stdout}") + raise + def main(): parser = argparse.ArgumentParser(description="Repack esptool and update JSON metadata.") parser.add_argument("version", help="Version of the esptool (e.g. 5.0.dev1)") parser.add_argument("-l", "--local", dest="base_folder", help="Enable local build mode and set the base folder with unpacked artifacts") + parser.add_argument("-c", "--commit", action="store_true", help="Automatically create a new branch and commit the JSON file changes") args = parser.parse_args() script_dir = Path(__file__).resolve().parent @@ -232,5 +257,10 @@ def main(): shutil.move(tmp_json_path, json_path) print(f"Done. JSON updated at {json_path}") + # Auto-commit if requested + if args.commit: + print("Auto-commit enabled. Creating branch and committing changes...") + create_branch_and_commit(args.version, json_path) + if __name__ == "__main__": main() diff --git a/.github/workflows/allboards.yml b/.github/workflows/allboards.yml index 6910ad05d3f..31eee587ca9 100644 --- a/.github/workflows/allboards.yml +++ b/.github/workflows/allboards.yml @@ -5,6 +5,9 @@ on: repository_dispatch: types: [test-boards] +permissions: + contents: read + jobs: find-boards: runs-on: ubuntu-latest @@ -36,10 +39,6 @@ jobs: with: ref: ${{ github.event.client_payload.branch }} - - run: npm install - - name: Setup jq - uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 - - id: set-test-chunks name: Set Chunks run: echo "test-chunks<> $GITHUB_OUTPUT diff --git a/.github/workflows/boards.yml b/.github/workflows/boards.yml index 287e97219c4..4ddb1443746 100644 --- a/.github/workflows/boards.yml +++ b/.github/workflows/boards.yml @@ -8,6 +8,9 @@ on: - "libraries/ESP32/examples/CI/CIBoardsTest/CIBoardsTest.ino" - ".github/workflows/boards.yml" +permissions: + contents: read + env: # It's convenient to set variables for values used multiple times in the workflow GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} @@ -24,9 +27,6 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Setup jq - uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 - - name: Get board name run: bash .github/scripts/find_new_boards.sh ${{ github.repository }} ${{github.base_ref}} diff --git a/.github/workflows/build_component.yml b/.github/workflows/build_component.yml new file mode 100644 index 00000000000..bc32f7a8999 --- /dev/null +++ b/.github/workflows/build_component.yml @@ -0,0 +1,226 @@ +name: Arduino as ESP-IDF Component + +on: + workflow_dispatch: + inputs: + idf_ver: + description: "IDF Versions" + default: "release-v5.3,release-v5.4,release-v5.5" + type: "string" + required: true + idf_targets: + description: "IDF Targets" + default: "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + type: "string" + required: false + push: + branches: + - master + - release/* + pull_request: + paths: + - "cores/**" + - "libraries/**/*.cpp" + - "libraries/**/*.c" + - "libraries/**/*.h" + - "idf_component_examples/**" + - "idf_component.yml" + - "Kconfig.projbuild" + - "CMakeLists.txt" + - ".github/workflows/build_component.yml" + - ".github/scripts/check-cmakelists.sh" + - ".github/scripts/on-push-idf.sh" + - ".github/scripts/sketch_utils.sh" + - ".github/scripts/get_affected.py" + - "variants/esp32/**" + - "variants/esp32c2/**" + - "variants/esp32c3/**" + - "variants/esp32c5/**" + - "variants/esp32c6/**" + - "variants/esp32h2/**" + - "variants/esp32p4/**" + - "variants/esp32s2/**" + - "variants/esp32s3/**" + - "!*.md" + - "!*.txt" + - "!*.properties" + +permissions: + contents: read + +concurrency: + group: build-component-${{github.event.pull_request.number || github.ref}} + cancel-in-progress: true + +jobs: + cmake-check: + name: Check CMakeLists + runs-on: ubuntu-latest + if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - run: bash ./.github/scripts/check-cmakelists.sh + + set-matrix: + name: Set Matrix + runs-on: ubuntu-latest + if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + should_build: ${{ steps.affected-examples.outputs.should_build }} + steps: + - name: Install universal-ctags + uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3 + with: + packages: libicu74 libjansson4 libxml2 libyaml-0-2 universal-ctags + version: 1 + execute_install_scripts: true + + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 2 + + - name: Get changed files + id: changed-files + uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 + + - name: Get affected examples + id: affected-examples + env: + IS_PR: ${{ github.event_name == 'pull_request' }} + run: | + (which ctags-universal || which ctags) || (echo "Error: Neither ctags-universal nor ctags found in PATH" && exit 1) + python3 ./.github/scripts/get_affected.py --debug --component ${{ steps.changed-files.outputs.all_changed_files }} > affected_examples.txt + + - name: Upload affected examples + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: affected_examples + path: affected_examples.txt + if-no-files-found: error + + - name: Upload debug artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: get_affected_debug + path: | + ctags_defs_by_qname.json + ctags_header_to_qnames.json + ctags_tags.jsonl + dependencies.json + dependencies_reverse.json + if-no-files-found: warn + + - name: Get Matrix Combinations + id: set-matrix + run: | + # Define version-specific target configurations + get_targets_for_version() { + case "$1" in + "release-v5.3") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + ;; + "release-v5.4") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4" + ;; + "release-v5.5") + echo "esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c5,esp32c6,esp32h2,esp32p4" + ;; + *) + echo "" + ;; + esac + } + + # Default versions if not provided via inputs + DEFAULT_VERSIONS="release-v5.3,release-v5.4,release-v5.5" + + # Use inputs if provided, otherwise use defaults + if [[ -n "${{ inputs.idf_ver }}" ]]; then + VERSIONS="${{ inputs.idf_ver }}" + else + VERSIONS="$DEFAULT_VERSIONS" + fi + + # Generate matrix combinations + echo '{"include": [' > matrix.json + first=true + IFS=',' read -ra VERSION_ARRAY <<< "$VERSIONS" + + for version in "${VERSION_ARRAY[@]}"; do + # Trim whitespace + version=$(echo "$version" | xargs) + + # Get targets for this version + if [[ -n "${{ inputs.idf_targets }}" ]]; then + # Use provided targets for all versions + targets="${{ inputs.idf_targets }}" + else + # Use version-specific targets + targets=$(get_targets_for_version "$version") + fi + + if [[ -n "$targets" ]]; then + IFS=',' read -ra TARGET_ARRAY <<< "$targets" + for target in "${TARGET_ARRAY[@]}"; do + # Trim whitespace + target=$(echo "$target" | xargs) + + if [ "$first" = true ]; then + first=false + else + echo ',' >> matrix.json + fi + echo "{\"idf_ver\": \"$version\", \"idf_target\": \"$target\"}" >> matrix.json + done + fi + done + echo ']}' >> matrix.json + + # Debug: Print the matrix for verification + echo "Debug - Generated matrix:" + cat matrix.json | jq . + + # Set output + printf "matrix=%s\n" "$(cat matrix.json | jq -c .)" >> $GITHUB_OUTPUT + + build-esp-idf-component: + name: Build IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }} + runs-on: ubuntu-latest + needs: set-matrix + if: ${{ needs.set-matrix.outputs.should_build == '1' }} + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.set-matrix.outputs.matrix) }} + container: espressif/idf:${{ matrix.idf_ver }} + steps: + - name: Check out arduino-esp32 as a component + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: recursive + path: components/arduino-esp32 + + # Need to install jq in the container to be able to use it in the script + - name: Setup jq + uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 + + - name: Download affected examples + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + with: + name: affected_examples + + - name: Build + env: + IDF_TARGET: ${{ matrix.idf_target }} + shell: bash + run: | + chmod a+x ./components/arduino-esp32/.github/scripts/* + ./components/arduino-esp32/.github/scripts/on-push-idf.sh affected_examples.txt + + - name: Upload generated sdkconfig files for debugging + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: always() + with: + name: sdkconfig-${{ matrix.idf_ver }}-${{ matrix.idf_target }} + path: ./components/arduino-esp32/idf_component_examples/**/sdkconfig diff --git a/.github/workflows/build_py_tools.yml b/.github/workflows/build_py_tools.yml index bbb36589c84..e22d8df5eff 100644 --- a/.github/workflows/build_py_tools.yml +++ b/.github/workflows/build_py_tools.yml @@ -9,6 +9,10 @@ on: - "tools/gen_esp32part.py" - "tools/gen_insights_package.py" +permissions: + contents: write + pull-requests: read + jobs: find-changed-tools: name: Check if tools have been changed diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000000..26bd868c190 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,94 @@ +name: CodeQL Analysis + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + paths: + - "**/*.c" + - "**/*.cpp" + - "**/*.h" + - "**/*.ino" + - "**/*.py" + - ".github/workflows/*.yml" + - ".github/workflows/*.yaml" + +permissions: + actions: read + contents: read + pull-requests: read + security-events: write + +jobs: + codeql-analysis: + name: CodeQL ${{ matrix.language }} analysis + runs-on: ubuntu-latest + strategy: + matrix: + language: [python, actions, cpp] + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Process .ino files + if: matrix.language == 'cpp' + run: | + # Create a mapping file to track renamed files + echo "{}" > renamed_files.json + + # Find all .ino files and process them + find . -name "*.ino" -type f | while read -r file; do + echo "Processing $file" + + # Get the relative path from repository root + rel_path=$(realpath --relative-to=. "$file") + cpp_path="${rel_path%.ino}.cpp" + + # Create new .cpp file with Arduino.h include + echo "#include " > "$cpp_path" + + # Append the original content + cat "$file" >> "$cpp_path" + + # Update the mapping file + jq --arg ino "$rel_path" --arg cpp "$cpp_path" '. += {($cpp): $ino}' renamed_files.json > temp.json && mv temp.json renamed_files.json + + # Remove the original .ino file + rm "$file" + + echo "Converted $file to $cpp_path" + done + + echo "Renamed files mapping:" + cat renamed_files.json + + - name: Initialize CodeQL + uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + with: + build-mode: none + languages: ${{ matrix.language }} + config-file: ./.github/codeql/codeql-config.yml + + - name: Run CodeQL Analysis + uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + with: + category: "/language:${{ matrix.language }}" + output: sarif-results + upload: failure-only + + - name: Process SARIF file + if: matrix.language == 'cpp' + run: | + sarif_file="sarif-results/${{ matrix.language }}.sarif" + + # Run the Python script to process the SARIF file + python3 .github/scripts/process_sarif.py "$sarif_file" "renamed_files.json" + + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2 + with: + sarif_file: sarif-results/${{ matrix.language }}.sarif + category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/docs_build.yml b/.github/workflows/docs_build.yml index d9b9f160228..5253c166f85 100644 --- a/.github/workflows/docs_build.yml +++ b/.github/workflows/docs_build.yml @@ -13,6 +13,9 @@ on: - "docs/**" - ".github/workflows/docs_build.yml" +permissions: + contents: read + jobs: build-docs: name: Build ESP-Docs diff --git a/.github/workflows/docs_deploy.yml b/.github/workflows/docs_deploy.yml index 9f45e35aef8..0c54d24aaf9 100644 --- a/.github/workflows/docs_deploy.yml +++ b/.github/workflows/docs_deploy.yml @@ -13,6 +13,9 @@ on: - "docs/**" - ".github/workflows/docs_deploy.yml" +permissions: + contents: read + jobs: deploy-prod-docs: name: Deploy Documentation on Production diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index 60795229eff..5dcb46dc36a 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -10,6 +10,10 @@ on: - ".github/scripts/on-pages.sh" - ".github/workflows/gh-pages.yml" +permissions: + contents: write + pages: write + jobs: build-pages: name: Build GitHub Pages diff --git a/.github/workflows/lib.yml b/.github/workflows/lib.yml index 0cb50842e5d..8af0992ebea 100644 --- a/.github/workflows/lib.yml +++ b/.github/workflows/lib.yml @@ -13,6 +13,11 @@ concurrency: group: libs-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: write + pull-requests: read + pages: write + env: # It's convenient to set variables for values used multiple times in the workflow SKETCHES_REPORTS_PATH: libraries-report diff --git a/.github/workflows/pre-commit-status.yml b/.github/workflows/pre-commit-status.yml index c7be9f8d352..afd52fdcb5e 100644 --- a/.github/workflows/pre-commit-status.yml +++ b/.github/workflows/pre-commit-status.yml @@ -9,6 +9,7 @@ on: permissions: statuses: write + pull-requests: write jobs: report-success: @@ -62,3 +63,92 @@ jobs: target_url: 'https://github.com/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}' })).data; core.info(`${name} is ${state}`); + + manage-labels: + name: Manage PR labels + if: github.event.workflow_run.event == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: Download and Extract Artifacts + uses: dawidd6/action-download-artifact@07ab29fd4a977ae4d2b275087cf67563dfdf0295 # v9 + continue-on-error: true + with: + run_id: ${{ github.event.workflow_run.id }} + name: pr-artifacts + path: ./pr-artifacts + + - name: Get PR information + id: pr-info + run: | + if [ -f "./pr-artifacts/pr_number.txt" ]; then + pr_number=$(cat ./pr-artifacts/pr_number.txt | tr -cd '[:digit:]') + pre_commit_outcome=$(cat ./pr-artifacts/pre_commit_outcome.txt | tr -cd '[:alpha:]_') + pending_commit=$(cat ./pr-artifacts/pending_commit.txt | tr -cd '[:digit:]') + has_retrigger_label=$(cat ./pr-artifacts/has_retrigger_label.txt | tr -cd '[:alpha:]') + + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + echo "pre_commit_outcome=$pre_commit_outcome" >> $GITHUB_OUTPUT + echo "pending_commit=$pending_commit" >> $GITHUB_OUTPUT + echo "has_retrigger_label=$has_retrigger_label" >> $GITHUB_OUTPUT + echo "artifacts_found=true" >> $GITHUB_OUTPUT + + echo "PR number: $pr_number" + echo "Pre-commit outcome: $pre_commit_outcome" + echo "Pending commit: $pending_commit" + echo "Has retrigger label: $has_retrigger_label" + else + echo "No PR artifacts found" + echo "artifacts_found=false" >> $GITHUB_OUTPUT + fi + + - name: Remove re-trigger label if it was present + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.has_retrigger_label == 'true' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label 'Re-trigger Pre-commit Hooks' + env: + GH_TOKEN: ${{ github.token }} + + - name: Add label if pre-commit fixes are required + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'failure' && + steps.pr-info.outputs.pending_commit == '0' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --add-label 'Status: Pre-commit fixes required ⚠️' + env: + GH_TOKEN: ${{ github.token }} + + - name: Remove label if pre-commit was successful + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'success' + continue-on-error: true + run: | + gh pr edit ${{ steps.pr-info.outputs.pr_number }} --repo ${{ github.repository }} --remove-label 'Status: Pre-commit fixes required ⚠️' + env: + GH_TOKEN: ${{ github.token }} + + - name: Comment on PR about pre-commit failures + if: | + steps.pr-info.outputs.artifacts_found == 'true' && + steps.pr-info.outputs.pre_commit_outcome == 'failure' && + steps.pr-info.outputs.pending_commit == '0' + uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1 + with: + pr-number: ${{ steps.pr-info.outputs.pr_number }} + message: | + ## ⚠️ Pre-commit Hooks Failed + + Some pre-commit hooks failed and require manual fixes. Please see the detailed error report below. + + **What to do:** + 1. 📋 [**View the detailed error report**](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.event.workflow_run.id }}) to see which hooks failed + 2. 🔧 Fix the issues locally in your code + 3. 💾 Commit and push your changes + 4. 🔄 The hooks will run again automatically + + **Need help?** Ask in the comments below. diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index a3b858dd0fb..30bbc3c3e41 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,6 +12,10 @@ concurrency: group: pre-commit-${{github.event.pull_request.number || github.ref}} cancel-in-progress: true +permissions: + contents: read + pull-requests: write + jobs: lint: if: | @@ -27,12 +31,6 @@ jobs: with: fetch-depth: 2 - - name: Remove Label - if: contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit Hooks') - run: gh pr edit ${{ github.event.number }} --remove-label 'Re-trigger Pre-commit Hooks' - env: - GH_TOKEN: ${{ github.token }} - - name: Set up Python 3 uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.0.4 with: @@ -61,6 +59,7 @@ jobs: uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - name: Run pre-commit hooks in changed files + id: pre-commit run: pre-commit run --color=always --show-diff-on-failure --files ${{ steps.changed-files.outputs.all_changed_files }} - name: Save pre-commit cache @@ -78,3 +77,21 @@ jobs: if: ${{ always() && github.event_name == 'pull_request' }} with: msg: "ci(pre-commit): Apply automatic fixes" + + - name: Save workflow information for labeling + if: ${{ always() && github.event_name == 'pull_request' }} + run: | + mkdir -p ./pr-artifacts + echo "${{ github.event.number }}" > ./pr-artifacts/pr_number.txt + echo "${{ steps.pre-commit.outcome }}" > ./pr-artifacts/pre_commit_outcome.txt + echo "${{ steps.pre-commit.outputs.pending_commit }}" > ./pr-artifacts/pending_commit.txt + echo "${{ contains(github.event.pull_request.labels.*.name, 'Re-trigger Pre-commit Hooks') }}" > ./pr-artifacts/has_retrigger_label.txt + + - name: Upload PR artifacts + if: ${{ always() && github.event_name == 'pull_request' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: pr-artifacts + path: ./pr-artifacts/ + retention-days: 1 + diff --git a/.github/workflows/publishlib.yml b/.github/workflows/publishlib.yml index 0e1c3f64afd..f97a2e3b5f5 100644 --- a/.github/workflows/publishlib.yml +++ b/.github/workflows/publishlib.yml @@ -12,6 +12,10 @@ env: SKETCHES_REPORTS_PATH: artifacts/libraries-report GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: read + pull-requests: write + jobs: lib-test-results: name: External Libraries Test Results diff --git a/.github/workflows/publishsizes-2.x.yml b/.github/workflows/publishsizes-2.x.yml index 738e215bc3f..f912445e622 100644 --- a/.github/workflows/publishsizes-2.x.yml +++ b/.github/workflows/publishsizes-2.x.yml @@ -9,6 +9,10 @@ env: RESULT_SIZES_TEST_FILE: SIZES_TEST.md GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: write + pull-requests: write + jobs: sizes-test-results: name: Sizes Comparison Results diff --git a/.github/workflows/publishsizes.yml b/.github/workflows/publishsizes.yml index fad2418668c..611ee741efa 100644 --- a/.github/workflows/publishsizes.yml +++ b/.github/workflows/publishsizes.yml @@ -12,6 +12,11 @@ env: SKETCHES_REPORTS_PATH: artifacts/sizes-report GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} +permissions: + contents: read + pull-requests: write + pages: write + jobs: sizes-test-results: name: Sizes Comparison Results diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 1523b7231be..690993504c2 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -25,63 +25,63 @@ on: pull_request: paths: - "cores/**" - - "libraries/**" - - "!libraries/**.md" - - "!libraries/**.txt" - - "!libraries/**.properties" - - "!libraries/**.py" + - "libraries/**/*.cpp" + - "libraries/**/*.c" + - "libraries/**/*.h" + - "libraries/**/*.ino" + - "libraries/**/ci.json" - "package/**" - - "idf_component_examples/**" - - "tools/**.py" + - "tools/get.*" - "platform.txt" - "programmers.txt" - - "idf_component.yml" - - "Kconfig.projbuild" - "package.json" - - "CMakeLists.txt" - ".github/workflows/push.yml" - - ".github/scripts/**" - - "!.github/scripts/find_*" - - "!.github/scripts/on-release.sh" - - "!.github/scripts/tests_*" - - "!.github/scripts/upload_*" - - "variants/esp32/**/*" - - "variants/esp32c3/**/*" - - "variants/esp32c5/**/*" - - "variants/esp32c6/**/*" - - "variants/esp32h2/**/*" - - "variants/esp32p4/**/*" - - "variants/esp32s2/**/*" - - "variants/esp32s3/**/*" + - ".github/scripts/install-*" + - ".github/scripts/on-push.sh" + - ".github/scripts/sketch_utils.sh" + - ".github/scripts/get_affected.py" + - "variants/esp32/**" + - "variants/esp32c3/**" + - "variants/esp32c5/**" + - "variants/esp32c6/**" + - "variants/esp32h2/**" + - "variants/esp32p4/**" + - "variants/esp32s2/**" + - "variants/esp32s3/**" + - "!*.md" + - "!*.txt" + - "!*.properties" concurrency: group: build-${{github.event.pull_request.number || github.ref}} cancel-in-progress: true +permissions: + contents: write + pull-requests: read + pages: write + env: MAX_CHUNKS: 15 jobs: - cmake-check: - name: Check cmake file - runs-on: ubuntu-latest - if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - run: bash ./.github/scripts/check-cmakelists.sh - gen-chunks: name: Generate chunks runs-on: ubuntu-latest if: ${{ !(github.event_name == 'pull_request' && startsWith(github.head_ref, 'release/')) }} outputs: - build_all: ${{ steps.set-chunks.outputs.build_all }} - build_libraries: ${{ steps.set-chunks.outputs.build_libraries }} - build_static_sketches: ${{ steps.set-chunks.outputs.build_static_sketches }} - build_idf: ${{ steps.set-chunks.outputs.build_idf }} + should_build: ${{ steps.set-chunks.outputs.should_build }} + recompile_preset: ${{ steps.set-chunks.outputs.recompile_preset }} chunk_count: ${{ steps.set-chunks.outputs.chunk_count }} chunks: ${{ steps.set-chunks.outputs.chunks }} steps: + - name: Install universal-ctags + uses: awalsh128/cache-apt-pkgs-action@2c09a5e66da6c8016428a2172bd76e5e4f14bb17 # v1.5.3 + with: + packages: libicu74 libjansson4 libxml2 libyaml-0-2 universal-ctags + version: 1 + execute_install_scripts: true + - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: @@ -90,72 +90,39 @@ jobs: - name: Get changed files id: changed-files uses: tj-actions/changed-files@2f7c5bfce28377bc069a65ba478de0a74aa0ca32 # v46.0.1 - with: - files_yaml: | - core: - - '.github/**' - - 'cores/**' - - 'package/**' - - 'tools/**' - - 'platform.txt' - - 'programmers.txt' - - "variants/esp32/**/*" - - "variants/esp32c3/**/*" - - "variants/esp32c6/**/*" - - "variants/esp32h2/**/*" - - "variants/esp32p4/**/*" - - "variants/esp32s2/**/*" - - "variants/esp32s3/**/*" - libraries: - - 'libraries/**/examples/**' - - 'libraries/**/src/**' - networking: - - 'libraries/Network/src/**' - fs: - - 'libraries/FS/src/**' - static_sketeches: - - 'libraries/NetworkClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino' - - 'libraries/BLE/examples/Server/Server.ino' - - 'libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino' - - 'libraries/Insights/examples/MinimalDiagnostics/MinimalDiagnostics.ino' - - 'libraries/NetworkClientSecure/src/**' - - 'libraries/BLE/src/**' - - 'libraries/Insights/src/**' - idf: - - 'idf_component.yml' - - 'Kconfig.projbuild' - - 'CMakeLists.txt' - - "idf_component_examples/**" - name: Set chunks id: set-chunks env: - LIB_FILES: ${{ steps.changed-files.outputs.libraries_all_changed_files }} IS_PR: ${{ github.event_name == 'pull_request' }} MAX_CHUNKS: ${{ env.MAX_CHUNKS }} - BUILD_IDF: ${{ steps.changed-files.outputs.idf_any_changed == 'true' }} - BUILD_LIBRARIES: ${{ steps.changed-files.outputs.libraries_any_changed == 'true' }} - BUILD_STATIC_SKETCHES: ${{ steps.changed-files.outputs.static_sketeches_any_changed == 'true' }} - FS_CHANGED: ${{ steps.changed-files.outputs.fs_any_changed == 'true' }} - NETWORKING_CHANGED: ${{ steps.changed-files.outputs.networking_any_changed == 'true' }} - CORE_CHANGED: ${{ steps.changed-files.outputs.core_any_changed == 'true' }} - LIB_CHANGED: ${{ steps.changed-files.outputs.libraries_any_changed == 'true' }} run: | - bash ./.github/scripts/set_push_chunks.sh + (which ctags-universal || which ctags) || (echo "Error: Neither ctags-universal nor ctags found in PATH" && exit 1) + python3 ./.github/scripts/get_affected.py --debug ${{ steps.changed-files.outputs.all_changed_files }} > affected_sketches.txt - - name: Upload sketches found - if: ${{ steps.set-chunks.outputs.build_all == 'false' && steps.set-chunks.outputs.build_libraries == 'true' }} + - name: Upload affected sketches uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: sketches_found - path: sketches_found.txt - overwrite: true + name: affected_sketches + path: affected_sketches.txt if-no-files-found: error + - name: Upload debug artifacts + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: get_affected_debug + path: | + ctags_defs_by_qname.json + ctags_header_to_qnames.json + ctags_tags.jsonl + dependencies.json + dependencies_reverse.json + if-no-files-found: warn + # Ubuntu build-arduino-linux: name: Arduino ${{ matrix.chunk }} on ubuntu-latest - if: ${{ needs.gen-chunks.outputs.build_all == 'true' || needs.gen-chunks.outputs.build_libraries == 'true' }} + if: ${{ needs.gen-chunks.outputs.should_build == '1' }} needs: gen-chunks runs-on: ubuntu-latest strategy: @@ -192,19 +159,13 @@ jobs: echo "LOG_LEVEL=none" >> $GITHUB_ENV fi - - name: Build all sketches - if: ${{ needs.gen-chunks.outputs.build_all == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ env.MAX_CHUNKS }} 1 ${{ env.LOG_LEVEL }} - - - name: Download sketches found - if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} + - name: Download affected sketches uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: - name: sketches_found + name: affected_sketches - name: Build selected sketches - if: ${{ needs.gen-chunks.outputs.build_all == 'false' && needs.gen-chunks.outputs.build_libraries == 'true' }} - run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 ${{ env.LOG_LEVEL }} sketches_found.txt + run: bash ./.github/scripts/on-push.sh ${{ matrix.chunk }} ${{ needs.gen-chunks.outputs.chunk_count }} 1 ${{ env.LOG_LEVEL }} affected_sketches.txt #Upload cli compile json as artifact - name: Upload cli compile json @@ -218,7 +179,7 @@ jobs: build-arduino-win-mac: name: Arduino on ${{ matrix.os }} needs: gen-chunks - if: ${{ needs.gen-chunks.outputs.build_all == 'true' || needs.gen-chunks.outputs.build_static_sketches == 'true' }} + if: ${{ needs.gen-chunks.outputs.recompile_preset == '1' }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -233,59 +194,6 @@ jobs: - name: Build Sketches run: bash ./.github/scripts/on-push.sh - build-esp-idf-component: - name: Build with ESP-IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }} - needs: gen-chunks - if: | - needs.gen-chunks.outputs.build_all == 'true' || - needs.gen-chunks.outputs.build_libraries == 'true' || - needs.gen-chunks.outputs.build_idf == 'true' - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - # The version names here correspond to the versions of espressif/idf Docker image. - # See https://hub.docker.com/r/espressif/idf/tags and - # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html - # for details. - idf_ver: ["release-v5.3","release-v5.4","release-v5.5"] - idf_target: - [ - "esp32", - "esp32s2", - "esp32s3", - "esp32c2", - "esp32c3", - "esp32c6", - "esp32h2", - "esp32p4" - ] - container: espressif/idf:${{ matrix.idf_ver }} - steps: - - name: Check out arduino-esp32 as a component - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - submodules: recursive - path: components/arduino-esp32 - - - name: Setup jq - uses: dcarbone/install-jq-action@e397bd87438d72198f81efd21f876461183d383a # v3.0.1 - - - name: Build - env: - IDF_TARGET: ${{ matrix.idf_target }} - shell: bash - run: | - chmod a+x ./components/arduino-esp32/.github/scripts/* - ./components/arduino-esp32/.github/scripts/on-push-idf.sh - - - name: Upload generated sdkconfig files for debugging - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - if: always() - with: - name: sdkconfig-${{ matrix.idf_ver }}-${{ matrix.idf_target }} - path: ./components/arduino-esp32/idf_component_examples/**/sdkconfig - # Save artifacts to gh-pages save-master-artifacts: name: Save master artifacts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b23c80c49a..ddefdf69258 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: release: types: published +permissions: + contents: write + pages: write + jobs: build: name: Publish Release diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ddc9b64aace..557de11b509 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,27 +16,28 @@ on: pull_request: types: [opened, reopened, closed, synchronize, labeled, unlabeled] paths: + - ".github/scripts/install*.sh" - ".github/workflows/tests*" - - ".github/scripts/*.sh" - - "!.github/scripts/check-cmakelists.sh" - - "!.github/scripts/find_*" - - "!.github/scripts/on-*.sh" - - "!.github/scripts/set_push_chunks.sh" - - "!.github/scripts/update-version.sh" - - "!.github/scripts/upload_py_tools.sh" + - ".github/scripts/sketch_utils.sh" - "tests/**" - "cores/**" - "libraries/*/src/**.cpp" - "libraries/*/src/**.h" - "libraries/*/src/**.c" - "package/**" + - "!*.md" + - "!*.properties" schedule: - - cron: "0 2 * * *" + - cron: "0 0 * * *" concurrency: group: tests-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true +permissions: + contents: read + pull-requests: read + jobs: push-event-file: name: Push event file diff --git a/.github/workflows/tests_build.yml b/.github/workflows/tests_build.yml index ac1f40644ed..bf5a33f538f 100644 --- a/.github/workflows/tests_build.yml +++ b/.github/workflows/tests_build.yml @@ -12,6 +12,9 @@ on: description: "Chip to build tests for" required: true +permissions: + contents: read + jobs: build-tests: name: Build ${{ inputs.type }} tests for ${{ inputs.chip }} @@ -24,7 +27,7 @@ jobs: if: github.event.pull_request.number != null uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-bin + key: test-${{ env.id }}-bin path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.elf @@ -71,7 +74,7 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-build.outputs.enabled == 'true' && github.event.pull_request.number != null with: - key: tests-${{ env.id }}-bin + key: test-${{ env.id }}-bin path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.elf @@ -81,7 +84,7 @@ jobs: - name: Upload ${{ inputs.chip }} ${{ inputs.type }} binaries as artifacts uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} + name: test-bin-${{ inputs.chip }}-${{ inputs.type }} overwrite: true path: | ~/.arduino/tests/${{ inputs.chip }}/**/build*.tmp/*.bin diff --git a/.github/workflows/tests_hw.yml b/.github/workflows/tests_hw.yml index 6f5fc67f7b9..d3b2ef79301 100644 --- a/.github/workflows/tests_hw.yml +++ b/.github/workflows/tests_hw.yml @@ -12,6 +12,9 @@ on: description: "Chip to run tests for" required: true +permissions: + contents: read + env: DEBIAN_FRONTEND: noninteractive @@ -39,7 +42,7 @@ jobs: if: github.event.pull_request.number != null uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-results-hw + key: test-${{ env.id }}-results-hw path: | tests/**/*.xml tests/**/result_*.json @@ -61,6 +64,7 @@ jobs: if: ${{ steps.check-tests.outputs.enabled == 'true' }} uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: + # Workaround for missing files in checkout sparse-checkout: | * @@ -84,7 +88,7 @@ jobs: if: ${{ steps.check-tests.outputs.enabled == 'true' }} uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} + name: test-bin-${{ inputs.chip }}-${{ inputs.type }} path: | ~/.arduino/tests/${{ inputs.chip }} @@ -102,7 +106,7 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-tests.outputs.enabled == 'true' && github.event.pull_request.number != null with: - key: tests-${{ env.id }}-results-hw + key: test-${{ env.id }}-results-hw path: | tests/**/*.xml tests/**/result_*.json @@ -111,7 +115,7 @@ jobs: uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: tests-results-hw-${{ inputs.chip }}-${{ inputs.type }} + name: test-results-hw-${{ inputs.chip }}-${{ inputs.type }} overwrite: true path: | tests/**/*.xml diff --git a/.github/workflows/tests_qemu.yml b/.github/workflows/tests_qemu.yml index fa3f874cbbb..9375f802073 100644 --- a/.github/workflows/tests_qemu.yml +++ b/.github/workflows/tests_qemu.yml @@ -10,6 +10,9 @@ on: required: true type: string +permissions: + contents: read + jobs: qemu-test: name: QEMU ${{ inputs.chip }} ${{ inputs.type }} tests @@ -23,7 +26,7 @@ jobs: if: github.event.pull_request.number != null uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-results-qemu + key: test-${{ env.id }}-results-qemu path: | tests/**/*.xml tests/**/result_*.json @@ -115,7 +118,7 @@ jobs: if: ${{ steps.check-tests.outputs.enabled == 'true' }} uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 with: - name: tests-bin-${{ inputs.chip }}-${{ inputs.type }} + name: test-bin-${{ inputs.chip }}-${{ inputs.type }} path: | ~/.arduino/tests/${{ inputs.chip }} @@ -127,7 +130,7 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-tests.outputs.enabled == 'true' && github.event.pull_request.number != null with: - key: tests-${{ env.id }}-results-qemu + key: test-${{ env.id }}-results-qemu path: | tests/**/*.xml tests/**/result_*.json @@ -136,7 +139,7 @@ jobs: uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: tests-results-qemu-${{ inputs.chip }}-${{ inputs.type }} + name: test-results-qemu-${{ inputs.chip }}-${{ inputs.type }} overwrite: true path: | tests/**/*.xml diff --git a/.github/workflows/tests_results.yml b/.github/workflows/tests_results.yml index ebba2a3aa08..ab4bb31fca5 100644 --- a/.github/workflows/tests_results.yml +++ b/.github/workflows/tests_results.yml @@ -7,7 +7,8 @@ on: - completed # No permissions by default -permissions: { contents: read } +permissions: + contents: read jobs: unit-test-results: @@ -113,7 +114,7 @@ jobs: with: script: | const ref = process.env.original_ref; - const key_prefix = 'tests-' + ref + '-'; + const key_prefix = 'test-' + ref + '-'; if (process.env.original_event == 'pull_request' && process.env.original_action != 'closed') { console.log('Skipping cache cleanup for open PR'); @@ -163,15 +164,16 @@ jobs: - name: Generate report if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled env: - REPORT_FILE: ./runtime-tests-results/RUNTIME_TESTS_REPORT.md + REPORT_FILE: ./runtime-test-results/RUNTIME_TEST_RESULTS.md WOKWI_RUN_ID: ${{ github.event.workflow_run.id }} BUILD_RUN_ID: ${{ env.original_run_id }} IS_FAILING: ${{ env.original_conclusion == 'failure' || env.original_conclusion == 'timed_out' || github.event.workflow_run.conclusion == 'failure' || github.event.workflow_run.conclusion == 'timed_out' || job.status == 'failure' }} run: | rm -rf artifacts $REPORT_FILE - mv -f ./unity_results.json ./runtime-tests-results/unity_results.json + mv -f ./unity_results.json ./runtime-test-results/unity_results.json touch $REPORT_FILE - python3 ./runtime-tests-results/table_generator.py ./runtime-tests-results/unity_results.json >> $REPORT_FILE + python3 ./runtime-test-results/table_generator.py ./runtime-test-results/unity_results.json >> $REPORT_FILE + mv -f ./test_results.json ./runtime-test-results/test_results.json - name: Generate badge if: ${{ !cancelled() && (env.original_event == 'schedule' || env.original_event == 'workflow_dispatch') }} # codespell:ignore cancelled @@ -179,7 +181,7 @@ jobs: with: label: Runtime Tests status: ${{ job.status == 'success' && 'passing' || 'failing' }} - output: runtime-tests-results/badge.svg + output: runtime-test-results/badge.svg color: ${{ job.status == 'success' && 'green' || 'red' }} style: flat @@ -190,6 +192,6 @@ jobs: git config user.email "41898282+github-actions[bot]@users.noreply.github.com" if [[ `git status --porcelain` ]]; then git add --all - git commit -m "Updated runtime tests report" + git commit -m "Updated runtime test results" git push origin HEAD:gh-pages fi diff --git a/.github/workflows/tests_wokwi.yml b/.github/workflows/tests_wokwi.yml index 03dd64fc0fb..cfea30a501d 100644 --- a/.github/workflows/tests_wokwi.yml +++ b/.github/workflows/tests_wokwi.yml @@ -7,7 +7,8 @@ on: - completed # No permissions by default -permissions: { contents: read } +permissions: + contents: read env: WOKWI_TIMEOUT: 600000 # Milliseconds @@ -135,17 +136,27 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - pattern: tests-results-hw-* + pattern: test-results-hw-* merge-multiple: true path: artifacts/results/hw + - name: Download and extract parent GitLab results + uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + continue-on-error: true + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + run-id: ${{ github.event.workflow_run.id }} + pattern: test-results-gitlab + merge-multiple: true + path: artifacts/results/gitlab + - name: Download and extract parent QEMU results uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 continue-on-error: true with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - pattern: tests-results-qemu-* + pattern: test-results-qemu-* merge-multiple: true path: artifacts/results/qemu @@ -221,7 +232,7 @@ jobs: if: needs.get-artifacts.outputs.pr_num uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: - key: tests-${{ env.id }}-results-wokwi + key: test-${{ env.id }}-results-wokwi path: | tests/**/*.xml tests/**/result_*.json @@ -274,7 +285,7 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} run-id: ${{ github.event.workflow_run.id }} - name: tests-bin-${{ matrix.chip }}-${{ matrix.type }} + name: test-bin-${{ matrix.chip }}-${{ matrix.type }} path: | ~/.arduino/tests/${{ matrix.chip }} @@ -289,7 +300,7 @@ jobs: uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: steps.check-tests.outputs.enabled == 'true' && needs.get-artifacts.outputs.pr_num with: - key: tests-${{ env.id }}-results-wokwi + key: test-${{ env.id }}-results-wokwi path: | tests/**/*.xml tests/**/result_*.json @@ -298,7 +309,7 @@ jobs: uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 if: always() with: - name: tests-results-wokwi-${{ matrix.chip }}-${{ matrix.type }} + name: test-results-wokwi-${{ matrix.chip }}-${{ matrix.type }} overwrite: true path: | tests/**/*.xml diff --git a/.gitignore b/.gitignore index d254d439834..66b8f1b8031 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,10 @@ libraries/Insights/examples/*/*.ino.zip # Ignore Lib Builder Docker run scripts /run.sh /run.ps1 + +# Ignore dependency analysis artifacts +/dependencies.json +/dependencies_reverse.json +/affected_sketches.txt +/affected_examples.txt +/ctags_* diff --git a/.gitlab/workflows/sample.yml b/.gitlab/workflows/sample.yml index 32b6fce042d..e20cecf9e9e 100644 --- a/.gitlab/workflows/sample.yml +++ b/.gitlab/workflows/sample.yml @@ -1,6 +1,16 @@ hello-world: stage: test - script: - - echo "Hello, World from GitLab CI!" rules: - if: $CI_PIPELINE_SOURCE == "push" + - if: $CI_PIPELINE_SOURCE == "web" + - if: $CI_PIPELINE_SOURCE == "trigger" + variables: + PIPELINE_TRIGGER_TOKEN: $CI_PIPELINE_TRIGGER_TOKEN + script: + - echo "Hello, World from GitLab CI!" + - echo "Hello World!" > sample_artifact.txt + artifacts: + name: "sample-artifact" + paths: + - sample_artifact.txt + expire_in: 1 day diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0d425c46eae..7709c257bb2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -108,3 +108,20 @@ repos: - id: vale language_version: "1.23.2" types_or: [markdown, rst] + + # Always leave this last to ensure that all hooks have already run + - repo: local + hooks: + - id: git-diff + name: git diff + entry: | + bash -c ' + if [ "$CI" = "true" ]; then + if git diff --exit-code; then + echo "pending_commit=0" >> $GITHUB_OUTPUT + else + echo "pending_commit=1" >> $GITHUB_OUTPUT + fi + fi + ' + language: system diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c9800dfe2b..d9b295dfa70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ set(CORE_SRCS cores/esp32/esp32-hal-cpu.c cores/esp32/esp32-hal-dac.c cores/esp32/esp32-hal-gpio.c + cores/esp32/esp32-hal-hosted.c cores/esp32/esp32-hal-i2c.c cores/esp32/esp32-hal-i2c-ng.c cores/esp32/esp32-hal-i2c-slave.c @@ -54,6 +55,7 @@ set(CORE_SRCS cores/esp32/freertos_stats.cpp cores/esp32/FunctionalInterrupt.cpp cores/esp32/HardwareSerial.cpp + cores/esp32/HashBuilder.cpp cores/esp32/HEXBuilder.cpp cores/esp32/IPAddress.cpp cores/esp32/libb64/cdecode.c @@ -62,7 +64,6 @@ set(CORE_SRCS cores/esp32/main.cpp cores/esp32/MD5Builder.cpp cores/esp32/Print.cpp - cores/esp32/SHA1Builder.cpp cores/esp32/stdlib_noniso.c cores/esp32/Stream.cpp cores/esp32/StreamString.cpp @@ -93,6 +94,7 @@ set(ARDUINO_ALL_LIBRARIES Ethernet FFat FS + Hash HTTPClient HTTPUpdate Insights @@ -154,6 +156,13 @@ set(ARDUINO_LIBRARY_FS_SRCS libraries/FS/src/FS.cpp libraries/FS/src/vfs_api.cpp) +set(ARDUINO_LIBRARY_Hash_SRCS + libraries/Hash/src/SHA1Builder.cpp + libraries/Hash/src/SHA2Builder.cpp + libraries/Hash/src/SHA3Builder.cpp + libraries/Hash/src/PBKDF2_HMACBuilder.cpp + ) + set(ARDUINO_LIBRARY_HTTPClient_SRCS libraries/HTTPClient/src/HTTPClient.cpp) set(ARDUINO_LIBRARY_HTTPUpdate_SRCS libraries/HTTPUpdate/src/HTTPUpdate.cpp) @@ -307,6 +316,7 @@ set(ARDUINO_LIBRARY_Zigbee_SRCS libraries/Zigbee/src/ep/ZigbeeBinary.cpp libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp libraries/Zigbee/src/ep/ZigbeeFanControl.cpp + libraries/Zigbee/src/ep/ZigbeeMultistate.cpp ) set(ARDUINO_LIBRARY_BLE_SRCS @@ -373,7 +383,7 @@ if(NOT CONFIG_ARDUINO_SELECTIVE_COMPILATION OR CONFIG_ARDUINO_SELECTIVE_OpenThre endif() endif() -if(IDF_TARGET STREQUAL "esp32p4") +if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3" OR IDF_TARGET STREQUAL "esp32p4") list(APPEND requires esp_driver_touch_sens) endif() diff --git a/README.md b/README.md index f40315c03cc..f8ff1f28eaa 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=push&label=Compilation%20Tests)](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Apush) [![Verbose Build Status](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/push.yml?branch=master&event=schedule&label=Compilation%20Tests%20(Verbose))](https://github.com/espressif/arduino-esp32/actions/workflows/push.yml?query=branch%3Amaster+event%3Aschedule) [![External Libraries Test](https://img.shields.io/github/actions/workflow/status/espressif/arduino-esp32/lib.yml?branch=master&event=schedule&label=External%20Libraries%20Test)](https://github.com/espressif/arduino-esp32/blob/gh-pages/LIBRARIES_TEST.md) -[![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/badge.svg)](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-tests-results/RUNTIME_TESTS_REPORT.md) +[![Runtime Tests](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-test-results/badge.svg)](https://github.com/espressif/arduino-esp32/blob/gh-pages/runtime-test-results/RUNTIME_TEST_RESULTS.md) ### Need help or have a question? Join the chat at [Discord](https://discord.gg/8xY6e9crwv) or [open a new Discussion](https://github.com/espressif/arduino-esp32/discussions) diff --git a/boards.txt b/boards.txt index 41723e1f0d7..876ef761c94 100644 --- a/boards.txt +++ b/boards.txt @@ -29,6 +29,8 @@ menu.LORAWAN_PREAMBLE_LENGTH=LoRaWan Preamble Length menu.SLOW_CLK_TPYE=Slow Clk Type(only for LoRaWAN) menu.einksize=E-Ink Display Size menu.NetworkLogLevel=Network Log Level +menu.DisplayModel=Display Model + ############################################################## ### DO NOT PUT BOARDS ABOVE THE OFFICIAL ESPRESSIF BOARDS! ### ############################################################## @@ -502,6 +504,10 @@ esp32p4.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 esp32p4.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) esp32p4.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB esp32p4.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 +esp32p4.menu.PartitionScheme.esp_sr_16=ESP SR 16M (3MB APP/7MB SPIFFS/2.9MB MODEL) +esp32p4.menu.PartitionScheme.esp_sr_16.upload.maximum_size=3145728 +esp32p4.menu.PartitionScheme.esp_sr_16.upload.extra_flags=0xD10000 {build.path}/srmodels.bin +esp32p4.menu.PartitionScheme.esp_sr_16.build.partitions=esp_sr_16 esp32p4.menu.PartitionScheme.custom=Custom esp32p4.menu.PartitionScheme.custom.build.partitions= esp32p4.menu.PartitionScheme.custom.upload.maximum_size=16777216 @@ -13747,16 +13753,16 @@ adafruit_metro_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_metro_esp32s2.menu.PSRAM.disabled=Disabled adafruit_metro_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_metro_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_metro_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_metro_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_metro_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14133,16 +14139,16 @@ adafruit_magtag29_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_magtag29_esp32s2.menu.PSRAM.disabled=Disabled adafruit_magtag29_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_magtag29_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_magtag29_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_magtag29_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_magtag29_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14316,16 +14322,16 @@ adafruit_funhouse_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_funhouse_esp32s2.menu.PSRAM.disabled=Disabled adafruit_funhouse_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_funhouse_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_funhouse_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_funhouse_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_funhouse_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14754,16 +14760,16 @@ adafruit_feather_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s2.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -14937,16 +14943,16 @@ adafruit_feather_esp32s2_tft.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s2_tft.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2_tft.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_tft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2_tft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2_tft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15120,16 +15126,16 @@ adafruit_feather_esp32s2_reversetft.menu.PSRAM.enabled.build.defines=-DBOARD_HAS adafruit_feather_esp32s2_reversetft.menu.PSRAM.disabled=Disabled adafruit_feather_esp32s2_reversetft.menu.PSRAM.disabled.build.defines= -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s2_reversetft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15329,16 +15335,16 @@ adafruit_feather_esp32s3.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15734,16 +15740,16 @@ adafruit_feather_esp32s3_tft.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3_tft.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3_tft.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3_tft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_tft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3_tft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3_tft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -15952,16 +15958,16 @@ adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi=OPI PSRAM adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_feather_esp32s3_reversetft.menu.PSRAM.opi.build.psram_type=opi -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.default.build.partitions=default adafruit_feather_esp32s3_reversetft.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -16575,16 +16581,16 @@ adafruit_qtpy_esp32s2.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM adafruit_qtpy_esp32s2.menu.PSRAM.disabled=Disabled adafruit_qtpy_esp32s2.menu.PSRAM.disabled.build.defines= -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_qtpy_esp32s2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_qtpy_esp32s2.menu.PartitionScheme.default.build.partitions=default adafruit_qtpy_esp32s2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -16971,16 +16977,16 @@ adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi=OPI PSRAM adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM adafruit_qtpy_esp32s3_n4r2.menu.PSRAM.opi.build.psram_type=opi -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 -adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.default.build.partitions=default adafruit_qtpy_esp32s3_n4r2.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) @@ -23006,6 +23012,187 @@ m5stack_cores3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +m5stack_tab5.name=M5Tab5 + +m5stack_tab5.bootloader.tool=esptool_py +m5stack_tab5.bootloader.tool.default=esptool_py + +m5stack_tab5.upload.tool=esptool_py +m5stack_tab5.upload.tool.default=esptool_py +m5stack_tab5.upload.tool.network=esp_ota + +m5stack_tab5.upload.maximum_size=1310720 +m5stack_tab5.upload.maximum_data_size=327680 +m5stack_tab5.upload.flags= +m5stack_tab5.upload.extra_flags= +m5stack_tab5.upload.use_1200bps_touch=false +m5stack_tab5.upload.wait_for_upload_port=false + +m5stack_tab5.serial.disableDTR=false +m5stack_tab5.serial.disableRTS=false + +m5stack_tab5.build.tarch=riscv32 +m5stack_tab5.build.target=esp +m5stack_tab5.build.mcu=esp32p4 +m5stack_tab5.build.core=esp32 +m5stack_tab5.build.variant=m5stack_tab5 +m5stack_tab5.build.board=M5STACK_TAB5 +m5stack_tab5.build.bootloader_addr=0x2000 + +m5stack_tab5.build.usb_mode=0 +m5stack_tab5.build.cdc_on_boot=0 +m5stack_tab5.build.msc_on_boot=0 +m5stack_tab5.build.dfu_on_boot=0 +m5stack_tab5.build.f_cpu=360000000L +m5stack_tab5.build.flash_size=16MB +m5stack_tab5.build.flash_freq=80m +m5stack_tab5.build.img_freq=80m +m5stack_tab5.build.flash_mode=qio +m5stack_tab5.build.boot=qio +m5stack_tab5.build.partitions=default +m5stack_tab5.build.defines= + +## IDE 2.0 Seems to not update the value +m5stack_tab5.menu.JTAGAdapter.default=Disabled +m5stack_tab5.menu.JTAGAdapter.default.build.copy_jtag_files=0 +m5stack_tab5.menu.JTAGAdapter.builtin=Integrated USB JTAG +m5stack_tab5.menu.JTAGAdapter.builtin.build.openocdscript=esp32p4-builtin.cfg +m5stack_tab5.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +m5stack_tab5.menu.JTAGAdapter.external=FTDI Adapter +m5stack_tab5.menu.JTAGAdapter.external.build.openocdscript=esp32p4-ftdi.cfg +m5stack_tab5.menu.JTAGAdapter.external.build.copy_jtag_files=1 +m5stack_tab5.menu.JTAGAdapter.bridge=ESP USB Bridge +m5stack_tab5.menu.JTAGAdapter.bridge.build.openocdscript=esp32p4-bridge.cfg +m5stack_tab5.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +m5stack_tab5.menu.PSRAM.enabled=Enabled +m5stack_tab5.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +m5stack_tab5.menu.PSRAM.disabled=Disabled +m5stack_tab5.menu.PSRAM.disabled.build.defines= + +m5stack_tab5.menu.USBMode.hwcdc=Hardware CDC and JTAG +m5stack_tab5.menu.USBMode.hwcdc.build.usb_mode=1 +m5stack_tab5.menu.USBMode.default=USB-OTG (TinyUSB) +m5stack_tab5.menu.USBMode.default.build.usb_mode=0 + +m5stack_tab5.menu.CDCOnBoot.cdc=Enabled +m5stack_tab5.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +m5stack_tab5.menu.CDCOnBoot.default=Disabled +m5stack_tab5.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +m5stack_tab5.menu.MSCOnBoot.default=Disabled +m5stack_tab5.menu.MSCOnBoot.default.build.msc_on_boot=0 +m5stack_tab5.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +m5stack_tab5.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +m5stack_tab5.menu.DFUOnBoot.default=Disabled +m5stack_tab5.menu.DFUOnBoot.default.build.dfu_on_boot=0 +m5stack_tab5.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +m5stack_tab5.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +m5stack_tab5.menu.UploadMode.default=UART0 / Hardware CDC +m5stack_tab5.menu.UploadMode.default.upload.use_1200bps_touch=false +m5stack_tab5.menu.UploadMode.default.upload.wait_for_upload_port=false +m5stack_tab5.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +m5stack_tab5.menu.UploadMode.cdc.upload.use_1200bps_touch=true +m5stack_tab5.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +m5stack_tab5.menu.PartitionScheme.default=Default (2 x 6.5 MB app, 3.6 MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.default.build.partitions=default_16MB +m5stack_tab5.menu.PartitionScheme.default.upload.maximum_size=6553600 +m5stack_tab5.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +m5stack_tab5.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +m5stack_tab5.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +m5stack_tab5.menu.PartitionScheme.minimal.build.partitions=minimal +m5stack_tab5.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +m5stack_tab5.menu.PartitionScheme.no_fs.build.partitions=no_fs +m5stack_tab5.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +m5stack_tab5.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.no_ota.build.partitions=no_ota +m5stack_tab5.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +m5stack_tab5.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +m5stack_tab5.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +m5stack_tab5.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +m5stack_tab5.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +m5stack_tab5.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +m5stack_tab5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +m5stack_tab5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +m5stack_tab5.menu.PartitionScheme.huge_app.build.partitions=huge_app +m5stack_tab5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +m5stack_tab5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +m5stack_tab5.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +m5stack_tab5.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +m5stack_tab5.menu.PartitionScheme.fatflash=16M Flash (2MB APP/12.5MB FATFS) +m5stack_tab5.menu.PartitionScheme.fatflash.build.partitions=ffat +m5stack_tab5.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB=16M Flash (3MB APP/9.9MB FATFS) +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +m5stack_tab5.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 +m5stack_tab5.menu.PartitionScheme.custom=Custom +m5stack_tab5.menu.PartitionScheme.custom.build.partitions= +m5stack_tab5.menu.PartitionScheme.custom.upload.maximum_size=16777216 + +## From https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/kconfig.html#config-esp-default-cpu-freq-mhz +m5stack_tab5.menu.CPUFreq.360=360MHz +m5stack_tab5.menu.CPUFreq.360.build.f_cpu=360000000L +m5stack_tab5.menu.CPUFreq.40=40MHz +m5stack_tab5.menu.CPUFreq.40.build.f_cpu=40000000L + +m5stack_tab5.menu.FlashMode.qio=QIO +m5stack_tab5.menu.FlashMode.qio.build.flash_mode=dio +m5stack_tab5.menu.FlashMode.qio.build.boot=qio +m5stack_tab5.menu.FlashMode.dio=DIO +m5stack_tab5.menu.FlashMode.dio.build.flash_mode=dio +m5stack_tab5.menu.FlashMode.dio.build.boot=dio + +m5stack_tab5.menu.FlashFreq.80=80MHz +m5stack_tab5.menu.FlashFreq.80.build.flash_freq=80m +m5stack_tab5.menu.FlashFreq.40=40MHz +m5stack_tab5.menu.FlashFreq.40.build.flash_freq=40m + +m5stack_tab5.menu.FlashSize.16M=16MB (128Mb) +m5stack_tab5.menu.FlashSize.16M.build.flash_size=16MB + +m5stack_tab5.menu.UploadSpeed.921600=921600 +m5stack_tab5.menu.UploadSpeed.921600.upload.speed=921600 +m5stack_tab5.menu.UploadSpeed.115200=115200 +m5stack_tab5.menu.UploadSpeed.115200.upload.speed=115200 +m5stack_tab5.menu.UploadSpeed.256000.windows=256000 +m5stack_tab5.menu.UploadSpeed.256000.upload.speed=256000 +m5stack_tab5.menu.UploadSpeed.230400.windows.upload.speed=256000 +m5stack_tab5.menu.UploadSpeed.230400=230400 +m5stack_tab5.menu.UploadSpeed.230400.upload.speed=230400 +m5stack_tab5.menu.UploadSpeed.460800.linux=460800 +m5stack_tab5.menu.UploadSpeed.460800.macosx=460800 +m5stack_tab5.menu.UploadSpeed.460800.upload.speed=460800 +m5stack_tab5.menu.UploadSpeed.512000.windows=512000 +m5stack_tab5.menu.UploadSpeed.512000.upload.speed=512000 + +m5stack_tab5.menu.DebugLevel.none=None +m5stack_tab5.menu.DebugLevel.none.build.code_debug=0 +m5stack_tab5.menu.DebugLevel.error=Error +m5stack_tab5.menu.DebugLevel.error.build.code_debug=1 +m5stack_tab5.menu.DebugLevel.warn=Warn +m5stack_tab5.menu.DebugLevel.warn.build.code_debug=2 +m5stack_tab5.menu.DebugLevel.info=Info +m5stack_tab5.menu.DebugLevel.info.build.code_debug=3 +m5stack_tab5.menu.DebugLevel.debug=Debug +m5stack_tab5.menu.DebugLevel.debug.build.code_debug=4 +m5stack_tab5.menu.DebugLevel.verbose=Verbose +m5stack_tab5.menu.DebugLevel.verbose.build.code_debug=5 + +m5stack_tab5.menu.EraseFlash.none=Disabled +m5stack_tab5.menu.EraseFlash.none.upload.erase_cmd= +m5stack_tab5.menu.EraseFlash.all=Enabled +m5stack_tab5.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + + m5stack_timer_cam.name=M5TimerCAM m5stack_timer_cam.bootloader.tool=esptool_py @@ -29391,6 +29578,96 @@ wesp32.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## +mant1s.name=Silicognition ManT1S + +mant1s.bootloader.tool=esptool_py +mant1s.bootloader.tool.default=esptool_py + +mant1s.upload.tool=esptool_py +mant1s.upload.tool.default=esptool_py +mant1s.upload.tool.network=esp_ota + +mant1s.upload.maximum_size=1310720 +mant1s.upload.maximum_data_size=2424832 +mant1s.upload.flags= +mant1s.upload.extra_flags= + +mant1s.serial.disableDTR=true +mant1s.serial.disableRTS=true + +mant1s.build.tarch=xtensa +mant1s.build.bootloader_addr=0x1000 +mant1s.build.target=esp32 +mant1s.build.mcu=esp32 +mant1s.build.core=esp32 +mant1s.build.variant=mant1s +mant1s.build.board=MANT1S + +mant1s.build.f_cpu=240000000L +mant1s.build.flash_mode=dio +mant1s.build.flash_size=8MB +mant1s.build.boot=dio +mant1s.build.partitions=default +mant1s.build.defines= + +mant1s.menu.FlashFreq.80=80MHz +mant1s.menu.FlashFreq.80.build.flash_freq=80m +mant1s.menu.FlashFreq.40=40MHz +mant1s.menu.FlashFreq.40.build.flash_freq=40m + +mant1s.menu.PSRAM.enabled=Enabled +mant1s.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM -mfix-esp32-psram-cache-issue -mfix-esp32-psram-cache-strategy=memw +mant1s.menu.PSRAM.enabled.build.extra_libs= +mant1s.menu.PSRAM.disabled=Disabled +mant1s.menu.PSRAM.disabled.build.defines= +mant1s.menu.PSRAM.disabled.build.extra_libs= + +mant1s.menu.PartitionScheme.default=8M OTA with large SPIFFS (1.25MB APP/5.3MB SPIFFS) +mant1s.menu.PartitionScheme.default.build.partitions=large_spiffs_8MB +mant1s.menu.PartitionScheme.default_large_app=8M large OTA app with SPIFFS (3MB APP/1.5MB SPIFFS) +mant1s.menu.PartitionScheme.default_large_app.build.partitions=default_8MB +mant1s.menu.PartitionScheme.default_large_app.upload.maximum_size=3342336 +mant1s.menu.PartitionScheme.default_fatfs=8M OTA with large FATFS (1.25MB APP/5.3MB FATFS) +mant1s.menu.PartitionScheme.default_fatfs.build.partitions=large_ffat_8MB +mant1s.menu.PartitionScheme.default_large_app_fatfs=8M large OTA app with FATFS (3MB APP/1.5MB FATFS) +mant1s.menu.PartitionScheme.default_large_app_fatfs.build.partitions=default_ffat_8MB +mant1s.menu.PartitionScheme.default_large_app_fatfs.upload.maximum_size=3342336 + +mant1s.menu.UploadSpeed.921600=921600 +mant1s.menu.UploadSpeed.921600.upload.speed=921600 +mant1s.menu.UploadSpeed.115200=115200 +mant1s.menu.UploadSpeed.115200.upload.speed=115200 +mant1s.menu.UploadSpeed.256000.windows=256000 +mant1s.menu.UploadSpeed.256000.upload.speed=256000 +mant1s.menu.UploadSpeed.230400.windows.upload.speed=256000 +mant1s.menu.UploadSpeed.230400=230400 +mant1s.menu.UploadSpeed.230400.upload.speed=230400 +mant1s.menu.UploadSpeed.460800.linux=460800 +mant1s.menu.UploadSpeed.460800.macosx=460800 +mant1s.menu.UploadSpeed.460800.upload.speed=460800 +mant1s.menu.UploadSpeed.512000.windows=512000 +mant1s.menu.UploadSpeed.512000.upload.speed=512000 + +mant1s.menu.DebugLevel.none=None +mant1s.menu.DebugLevel.none.build.code_debug=0 +mant1s.menu.DebugLevel.error=Error +mant1s.menu.DebugLevel.error.build.code_debug=1 +mant1s.menu.DebugLevel.warn=Warn +mant1s.menu.DebugLevel.warn.build.code_debug=2 +mant1s.menu.DebugLevel.info=Info +mant1s.menu.DebugLevel.info.build.code_debug=3 +mant1s.menu.DebugLevel.debug=Debug +mant1s.menu.DebugLevel.debug.build.code_debug=4 +mant1s.menu.DebugLevel.verbose=Verbose +mant1s.menu.DebugLevel.verbose.build.code_debug=5 + +mant1s.menu.EraseFlash.none=Disabled +mant1s.menu.EraseFlash.none.upload.erase_cmd= +mant1s.menu.EraseFlash.all=Enabled +mant1s.menu.EraseFlash.all.upload.erase_cmd=-e + +############################################################## + t-beam.name=T-Beam t-beam.bootloader.tool=esptool_py @@ -32372,7 +32649,7 @@ ch_denky.menu.Revision.denkyd4.build.board=DENKY_PICOV3 ch_denky.menu.Revision.denkyd4.build.flash_size=8MB ch_denky.menu.Revision.denky32=WROOM32 ch_denky.menu.Revision.denky32.build.board=DENKY_WROOM32 -ch_denky.menu.Revision.denkyd4.build.flash_size=4MB +ch_denky.menu.Revision.denky32.build.flash_size=4MB ch_denky.menu.PartitionScheme.default=Default ch_denky.menu.PartitionScheme.default.build.partitions=default @@ -36120,6 +36397,200 @@ XIAO_ESP32C3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## + +XIAO_ESP32C5.name=XIAO_ESP32C5 + +XIAO_ESP32C5.vid.0=0x2886 +XIAO_ESP32C5.pid.0=0x0067 + +XIAO_ESP32C5.bootloader.tool=esptool_py +XIAO_ESP32C5.bootloader.tool.default=esptool_py + +XIAO_ESP32C5.upload.tool=esptool_py +XIAO_ESP32C5.upload.tool.default=esptool_py +XIAO_ESP32C5.upload.tool.network=esp_ota + +XIAO_ESP32C5.upload.maximum_size=1310720 +XIAO_ESP32C5.upload.maximum_data_size=327680 +XIAO_ESP32C5.upload.flags= +XIAO_ESP32C5.upload.extra_flags= +XIAO_ESP32C5.upload.use_1200bps_touch=false +XIAO_ESP32C5.upload.wait_for_upload_port=false + +XIAO_ESP32C5.serial.disableDTR=false +XIAO_ESP32C5.serial.disableRTS=false + +XIAO_ESP32C5.build.tarch=riscv32 +XIAO_ESP32C5.build.target=esp +XIAO_ESP32C5.build.mcu=esp32c5 +XIAO_ESP32C5.build.core=esp32 +XIAO_ESP32C5.build.variant=XIAO_ESP32C5 +XIAO_ESP32C5.build.board=XIAO_ESP32C5 +XIAO_ESP32C5.build.bootloader_addr=0x2000 + +XIAO_ESP32C5.build.cdc_on_boot=1 +XIAO_ESP32C5.build.f_cpu=240000000L +XIAO_ESP32C5.build.flash_size=8MB +XIAO_ESP32C5.build.flash_freq=80m +XIAO_ESP32C5.build.flash_mode=qio +XIAO_ESP32C5.build.boot=qio +XIAO_ESP32C5.build.partitions=default_8MB +XIAO_ESP32C5.build.defines= + +## IDE 2.0 Seems to not update the value +XIAO_ESP32C5.menu.JTAGAdapter.default=Disabled +XIAO_ESP32C5.menu.JTAGAdapter.default.build.copy_jtag_files=0 +XIAO_ESP32C5.menu.JTAGAdapter.builtin=Integrated USB JTAG +XIAO_ESP32C5.menu.JTAGAdapter.builtin.build.openocdscript=esp32c5-builtin.cfg +XIAO_ESP32C5.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +XIAO_ESP32C5.menu.JTAGAdapter.external=FTDI Adapter +XIAO_ESP32C5.menu.JTAGAdapter.external.build.openocdscript=esp32c5-ftdi.cfg +XIAO_ESP32C5.menu.JTAGAdapter.external.build.copy_jtag_files=1 +XIAO_ESP32C5.menu.JTAGAdapter.bridge=ESP USB Bridge +XIAO_ESP32C5.menu.JTAGAdapter.bridge.build.openocdscript=esp32c5-bridge.cfg +XIAO_ESP32C5.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +XIAO_ESP32C5.menu.PSRAM.disabled=Disabled +XIAO_ESP32C5.menu.PSRAM.disabled.build.defines= +XIAO_ESP32C5.menu.PSRAM.enabled=Enabled +XIAO_ESP32C5.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM + +XIAO_ESP32C5.menu.CDCOnBoot.cdc=Enabled +XIAO_ESP32C5.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +XIAO_ESP32C5.menu.CDCOnBoot.default=Disabled +XIAO_ESP32C5.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +XIAO_ESP32C5.menu.PartitionScheme.default_8MB=8M with spiffs (3MB APP/1.5MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.default_8MB.build.partitions=default_8MB +XIAO_ESP32C5.menu.PartitionScheme.default_8MB.upload.maximum_size=3342336 +XIAO_ESP32C5.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.default.build.partitions=default +XIAO_ESP32C5.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +XIAO_ESP32C5.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.minimal.build.partitions=minimal +XIAO_ESP32C5.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.no_ota.build.partitions=no_ota +XIAO_ESP32C5.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +XIAO_ESP32C5.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +XIAO_ESP32C5.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +XIAO_ESP32C5.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +XIAO_ESP32C5.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +XIAO_ESP32C5.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +XIAO_ESP32C5.menu.PartitionScheme.huge_app.build.partitions=huge_app +XIAO_ESP32C5.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +XIAO_ESP32C5.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB 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.fatflash=16M Flash (2MB APP/12.5MB FATFS) +XIAO_ESP32C5.menu.PartitionScheme.fatflash.build.partitions=ffat +XIAO_ESP32C5.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker=RainMaker 4MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +XIAO_ESP32C5.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB=RainMaker 8MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB.build.partitions=rainmaker_8MB +XIAO_ESP32C5.menu.PartitionScheme.rainmaker_8MB.upload.maximum_size=4116480 +XIAO_ESP32C5.menu.PartitionScheme.zigbee=Zigbee 4MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee.build.partitions=zigbee +XIAO_ESP32C5.menu.PartitionScheme.zigbee.upload.maximum_size=1310720 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB=Zigbee 8MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB.build.partitions=zigbee_8MB +XIAO_ESP32C5.menu.PartitionScheme.zigbee_8MB.upload.maximum_size=3407872 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB=Zigbee ZCZR 8MB with spiffs +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB.build.partitions=zigbee_zczr_8MB +XIAO_ESP32C5.menu.PartitionScheme.zigbee_zczr_8MB.upload.maximum_size=3407872 + +XIAO_ESP32C5.menu.CPUFreq.240=240MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.240.build.f_cpu=240000000L +XIAO_ESP32C5.menu.CPUFreq.160=160MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.160.build.f_cpu=160000000L +XIAO_ESP32C5.menu.CPUFreq.80=80MHz (WiFi) +XIAO_ESP32C5.menu.CPUFreq.80.build.f_cpu=80000000L +XIAO_ESP32C5.menu.CPUFreq.40=40MHz +XIAO_ESP32C5.menu.CPUFreq.40.build.f_cpu=40000000L +XIAO_ESP32C5.menu.CPUFreq.20=20MHz +XIAO_ESP32C5.menu.CPUFreq.20.build.f_cpu=20000000L +XIAO_ESP32C5.menu.CPUFreq.10=10MHz +XIAO_ESP32C5.menu.CPUFreq.10.build.f_cpu=10000000L + +XIAO_ESP32C5.menu.FlashMode.qio=QIO +XIAO_ESP32C5.menu.FlashMode.qio.build.flash_mode=dio +XIAO_ESP32C5.menu.FlashMode.qio.build.boot=qio +XIAO_ESP32C5.menu.FlashMode.dio=DIO +XIAO_ESP32C5.menu.FlashMode.dio.build.flash_mode=dio +XIAO_ESP32C5.menu.FlashMode.dio.build.boot=dio + +XIAO_ESP32C5.menu.FlashFreq.80=80MHz +XIAO_ESP32C5.menu.FlashFreq.80.build.flash_freq=80m +XIAO_ESP32C5.menu.FlashFreq.40=40MHz +XIAO_ESP32C5.menu.FlashFreq.40.build.flash_freq=40m + +XIAO_ESP32C5.menu.FlashSize.8M=8MB (64Mb) +XIAO_ESP32C5.menu.FlashSize.8M.build.flash_size=8MB + +XIAO_ESP32C5.menu.UploadSpeed.921600=921600 +XIAO_ESP32C5.menu.UploadSpeed.921600.upload.speed=921600 +XIAO_ESP32C5.menu.UploadSpeed.115200=115200 +XIAO_ESP32C5.menu.UploadSpeed.115200.upload.speed=115200 +XIAO_ESP32C5.menu.UploadSpeed.256000.windows=256000 +XIAO_ESP32C5.menu.UploadSpeed.256000.upload.speed=256000 +XIAO_ESP32C5.menu.UploadSpeed.230400.windows.upload.speed=256000 +XIAO_ESP32C5.menu.UploadSpeed.230400=230400 +XIAO_ESP32C5.menu.UploadSpeed.230400.upload.speed=230400 +XIAO_ESP32C5.menu.UploadSpeed.460800.linux=460800 +XIAO_ESP32C5.menu.UploadSpeed.460800.macosx=460800 +XIAO_ESP32C5.menu.UploadSpeed.460800.upload.speed=460800 +XIAO_ESP32C5.menu.UploadSpeed.512000.windows=512000 +XIAO_ESP32C5.menu.UploadSpeed.512000.upload.speed=512000 + +XIAO_ESP32C5.menu.DebugLevel.none=None +XIAO_ESP32C5.menu.DebugLevel.none.build.code_debug=0 +XIAO_ESP32C5.menu.DebugLevel.error=Error +XIAO_ESP32C5.menu.DebugLevel.error.build.code_debug=1 +XIAO_ESP32C5.menu.DebugLevel.warn=Warn +XIAO_ESP32C5.menu.DebugLevel.warn.build.code_debug=2 +XIAO_ESP32C5.menu.DebugLevel.info=Info +XIAO_ESP32C5.menu.DebugLevel.info.build.code_debug=3 +XIAO_ESP32C5.menu.DebugLevel.debug=Debug +XIAO_ESP32C5.menu.DebugLevel.debug.build.code_debug=4 +XIAO_ESP32C5.menu.DebugLevel.verbose=Verbose +XIAO_ESP32C5.menu.DebugLevel.verbose.build.code_debug=5 + +XIAO_ESP32C5.menu.EraseFlash.none=Disabled +XIAO_ESP32C5.menu.EraseFlash.none.upload.erase_cmd= +XIAO_ESP32C5.menu.EraseFlash.all=Enabled +XIAO_ESP32C5.menu.EraseFlash.all.upload.erase_cmd=-e + +XIAO_ESP32C5.menu.ZigbeeMode.default=Disabled +XIAO_ESP32C5.menu.ZigbeeMode.default.build.zigbee_mode= +XIAO_ESP32C5.menu.ZigbeeMode.default.build.zigbee_libs= +XIAO_ESP32C5.menu.ZigbeeMode.ed=Zigbee ED (end device) +XIAO_ESP32C5.menu.ZigbeeMode.ed.build.zigbee_mode=-DZIGBEE_MODE_ED +XIAO_ESP32C5.menu.ZigbeeMode.ed.build.zigbee_libs=-lesp_zb_api.ed -lzboss_stack.ed -lzboss_port.native +XIAO_ESP32C5.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +XIAO_ESP32C5.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +XIAO_ESP32C5.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.native +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug=Zigbee ED (end device) - Debug +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug.build.zigbee_mode=-DZIGBEE_MODE_ED +XIAO_ESP32C5.menu.ZigbeeMode.ed_debug.build.zigbee_libs=-lesp_zb_api.ed.debug -lzboss_stack.ed.debug -lzboss_port.native.debug +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug=Zigbee ZCZR (coordinator/router) - Debug +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +XIAO_ESP32C5.menu.ZigbeeMode.zczr_debug.build.zigbee_libs=-lesp_zb_api.zczr.debug -lzboss_stack.zczr.debug -lzboss_port.native.debug + +############################################################## + XIAO_ESP32C6.name=XIAO_ESP32C6 XIAO_ESP32C6.bootloader.tool=esptool_py @@ -36397,9 +36868,15 @@ XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.build.partitions=max_app_8MB XIAO_ESP32S3.menu.PartitionScheme.max_app_8MB.upload.maximum_size=8257536 XIAO_ESP32S3.menu.PartitionScheme.tinyuf2=TinyUF2 8MB (2MB APP/3.7MB FFAT) XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 -XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-8MB-tinyuf2 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-8MB XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 XIAO_ESP32S3.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota=TinyUF2 8MB No OTA (4MB APP/3.7MB FFAT) +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-8MB-noota +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=4194304 +XIAO_ESP32S3.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" + XIAO_ESP32S3.menu.CPUFreq.240=240MHz (WiFi) XIAO_ESP32S3.menu.CPUFreq.240.build.f_cpu=240000000L @@ -39699,7 +40176,7 @@ lionbits3.menu.EraseFlash.all.upload.erase_cmd=-e ############################################################## -gen4-ESP32-S3R8n16.name=4D Systems gen4-ESP32 16MB Modules (ESP32-S3R8n16) +gen4-ESP32-S3R8n16.name=4D Systems gen4-ESP32 Modules (ESP32-S3) gen4-ESP32-S3R8n16.bootloader.tool=esptool_py gen4-ESP32-S3R8n16.bootloader.tool.default=esptool_py @@ -39737,24 +40214,17 @@ gen4-ESP32-S3R8n16.build.flash_mode=dio gen4-ESP32-S3R8n16.build.boot=qio gen4-ESP32-S3R8n16.build.boot_freq=80m gen4-ESP32-S3R8n16.build.partitions=default -gen4-ESP32-S3R8n16.build.defines=-DBOARD_HAS_PSRAM +gen4-ESP32-S3R8n16.build.defines=-DBOARD_HAS_PSRAM -D{build.board} -D{build.DisplayModel} gen4-ESP32-S3R8n16.build.loop_core= gen4-ESP32-S3R8n16.build.event_core= gen4-ESP32-S3R8n16.build.psram_type=opi gen4-ESP32-S3R8n16.build.memory_type={build.boot}_{build.psram_type} -gen4-ESP32-S3R8n16.menu.PSRAM.opi=OPI PSRAM -gen4-ESP32-S3R8n16.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM -gen4-ESP32-S3R8n16.menu.PSRAM.opi.build.psram_type=opi - -gen4-ESP32-S3R8n16.menu.FlashMode.qio=QIO 80MHz -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.flash_mode=dio -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.boot=qio -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.boot_freq=80m -gen4-ESP32-S3R8n16.menu.FlashMode.qio.build.flash_freq=80m gen4-ESP32-S3R8n16.menu.FlashSize.16M=16MB (128Mb) gen4-ESP32-S3R8n16.menu.FlashSize.16M.build.flash_size=16MB +gen4-ESP32-S3R8n16.menu.FlashSize.32M=32MB (256Mb) +gen4-ESP32-S3R8n16.menu.FlashSize.32M.build.flash_size=32MB gen4-ESP32-S3R8n16.menu.LoopCore.1=Core 1 gen4-ESP32-S3R8n16.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 @@ -39805,6 +40275,15 @@ gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme3.upload.maximum_size=832 gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP) gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp gen4-ESP32-S3R8n16.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +gen4-ESP32-S3R8n16.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 gen4-ESP32-S3R8n16.menu.CPUFreq.240=240MHz (WiFi) gen4-ESP32-S3R8n16.menu.CPUFreq.240.build.f_cpu=240000000L @@ -39852,6 +40331,80 @@ gen4-ESP32-S3R8n16.menu.EraseFlash.none.upload.erase_cmd= gen4-ESP32-S3R8n16.menu.EraseFlash.all=Enabled gen4-ESP32-S3R8n16.menu.EraseFlash.all.upload.erase_cmd=-e +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24=gen4-ESP32-24 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24.build.DisplayModel=ESP32S3_24 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24t=gen4-ESP32-24T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24t.build.DisplayModel=ESP32S3_24T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct=gen4-ESP32-24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct.build.DisplayModel=ESP32S3_24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct_clb=gen4-ESP32-24CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_24ct_clb.build.DisplayModel=ESP32S3_24CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28=gen4-ESP32-28 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28.build.DisplayModel=ESP32S3_28 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28t=gen4-ESP32-28T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28t.build.DisplayModel=ESP32S3_28T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct=gen4-ESP32-28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct.build.DisplayModel=ESP32S3_28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct_clb=gen4-ESP32-28CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_28ct_clb.build.DisplayModel=ESP32S3_28CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32=gen4-ESP32-32 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32.build.DisplayModel=ESP32S3_32 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32t=gen4-ESP32-32T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32t.build.DisplayModel=ESP32S3_32T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct=gen4-ESP32-32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct.build.DisplayModel=ESP32S3_32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct_clb=gen4-ESP32-32CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_32ct_clb.build.DisplayModel=ESP32S3_32CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35=gen4-ESP32-35 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35.build.DisplayModel=ESP32S3_35 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35t=gen4-ESP32-35T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35t.build.DisplayModel=ESP32S3_35T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct=gen4-ESP32-35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct.build.DisplayModel=ESP32S3_35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct_clb=gen4-ESP32-35CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_35ct_clb.build.DisplayModel=ESP32S3_35CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43_qspi=gen4-ESP32Q-43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43_qspi.build.DisplayModel=ESP32S3_Q43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t_qspi=gen4-ESP32Q-43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t_qspi.build.DisplayModel=ESP32S3_Q43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_qspi=gen4-ESP32Q-43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_qspi.build.DisplayModel=ESP32S3_Q43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb_qspi=gen4-ESP32Q-43CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb_qspi.build.DisplayModel=ESP32S3_Q43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43=gen4-ESP32-43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43.build.DisplayModel=ESP32S3_43 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t=gen4-ESP32-43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43t.build.DisplayModel=ESP32S3_43T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct=gen4-ESP32-43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct.build.DisplayModel=ESP32S3_43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb=gen4-ESP32-43CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_43ct_clb.build.DisplayModel=ESP32S3_43CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50=gen4-ESP32-50 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50.build.DisplayModel=ESP32S3_50 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50t=gen4-ESP32-50T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50t.build.DisplayModel=ESP32S3_50T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct=gen4-ESP32-50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct.build.DisplayModel=ESP32S3_50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct_clb=gen4-ESP32-50CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_50ct_clb.build.DisplayModel=ESP32S3_50CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70=gen4-ESP32-70 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70.build.DisplayModel=ESP32S3_70 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70t=gen4-ESP32-70T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70t.build.DisplayModel=ESP32S3_70T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct=gen4-ESP32-70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct.build.DisplayModel=ESP32S3_70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct_clb=gen4-ESP32-70CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_70ct_clb.build.DisplayModel=ESP32S3_70CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90=ESP32-90 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90.build.DisplayModel=ESP32S3_90 +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90t=ESP32-90T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90t.build.DisplayModel=ESP32S3_90T +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct=ESP32-90CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct.build.DisplayModel=ESP32S3_90CT +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct_clb=ESP32-90CT-CLB +gen4-ESP32-S3R8n16.menu.DisplayModel.esp32s3_90ct_clb.build.DisplayModel=ESP32S3_90CT + + ############################################################## # Namino Rosso @@ -41520,6 +42073,196 @@ sensebox_mcu_esp32s2.menu.EraseFlash.none.upload.erase_cmd= sensebox_mcu_esp32s2.menu.EraseFlash.all=Enabled sensebox_mcu_esp32s2.menu.EraseFlash.all.upload.erase_cmd=-e +############################################################## +# senseBox Eye + +sensebox_eye.name=senseBox Eye +sensebox_eye.vid.0=0x303A +sensebox_eye.pid.0=0x82D1 +sensebox_eye.vid.1=0x303A +sensebox_eye.pid.1=0x82D2 +sensebox_eye.vid.2=0x303A +sensebox_eye.pid.2=0x82D3 + +sensebox_eye.bootloader.tool=esptool_py +sensebox_eye.bootloader.tool.default=esptool_py + +sensebox_eye.upload.tool=esptool_py +sensebox_eye.upload.tool.default=esptool_py +sensebox_eye.upload.tool.network=esp_ota + +sensebox_eye.upload.maximum_size=1310720 +sensebox_eye.upload.maximum_data_size=327680 +sensebox_eye.upload.flags= +sensebox_eye.upload.extra_flags= +sensebox_eye.upload.use_1200bps_touch=false +sensebox_eye.upload.wait_for_upload_port=false + +sensebox_eye.serial.disableDTR=false +sensebox_eye.serial.disableRTS=false + +sensebox_eye.build.tarch=xtensa +sensebox_eye.build.bootloader_addr=0x0 +sensebox_eye.build.target=esp32s3 +sensebox_eye.build.mcu=esp32s3 +sensebox_eye.build.core=esp32 +sensebox_eye.build.variant=sensebox_eye +sensebox_eye.build.board=SENSEBOX_EYE + +sensebox_eye.build.usb_mode=0 +sensebox_eye.build.cdc_on_boot=1 +sensebox_eye.build.msc_on_boot=1 +sensebox_eye.build.dfu_on_boot=0 +sensebox_eye.build.f_cpu=240000000L +sensebox_eye.build.flash_size=16MB (128Mb) +sensebox_eye.build.flash_freq=80m +sensebox_eye.build.flash_mode=dio +sensebox_eye.build.boot=qio +sensebox_eye.build.boot_freq=80m +sensebox_eye.build.partitions=tinyuf2 +sensebox_eye.build.defines= +sensebox_eye.build.loop_core= +sensebox_eye.build.event_core= +sensebox_eye.build.psram_type=qspi +sensebox_eye.build.memory_type={build.boot}_{build.psram_type} + +sensebox_eye.menu.JTAGAdapter.default=Disabled +sensebox_eye.menu.JTAGAdapter.default.build.copy_jtag_files=0 +sensebox_eye.menu.JTAGAdapter.builtin=Integrated USB JTAG +sensebox_eye.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +sensebox_eye.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +sensebox_eye.menu.JTAGAdapter.external=FTDI Adapter +sensebox_eye.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +sensebox_eye.menu.JTAGAdapter.external.build.copy_jtag_files=1 +sensebox_eye.menu.JTAGAdapter.bridge=ESP USB Bridge +sensebox_eye.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +sensebox_eye.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +sensebox_eye.menu.PSRAM.opi=OPI PSRAM +sensebox_eye.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM +sensebox_eye.menu.PSRAM.opi.build.psram_type=opi +sensebox_eye.menu.PSRAM.disabled=Disabled +sensebox_eye.menu.PSRAM.disabled.build.defines= +sensebox_eye.menu.PSRAM.disabled.build.psram_type=qspi + +sensebox_eye.menu.FlashMode.qio=QIO 80MHz +sensebox_eye.menu.FlashMode.qio.build.flash_mode=dio +sensebox_eye.menu.FlashMode.qio.build.boot=qio +sensebox_eye.menu.FlashMode.qio.build.boot_freq=80m +sensebox_eye.menu.FlashMode.qio.build.flash_freq=80m +sensebox_eye.menu.FlashMode.dio=DIO 80MHz +sensebox_eye.menu.FlashMode.dio.build.flash_mode=dio +sensebox_eye.menu.FlashMode.dio.build.boot=dio +sensebox_eye.menu.FlashMode.dio.build.boot_freq=80m +sensebox_eye.menu.FlashMode.dio.build.flash_freq=80m + +sensebox_eye.menu.FlashSize.16M=16MB (128Mb) +sensebox_eye.menu.FlashSize.16M.build.flash_size=16MB + +sensebox_eye.menu.LoopCore.1=Core 1 +sensebox_eye.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +sensebox_eye.menu.LoopCore.0=Core 0 +sensebox_eye.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +sensebox_eye.menu.EventsCore.1=Core 1 +sensebox_eye.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +sensebox_eye.menu.EventsCore.0=Core 0 +sensebox_eye.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +sensebox_eye.menu.USBMode.default=USB-OTG (TinyUSB) +sensebox_eye.menu.USBMode.default.build.usb_mode=0 +sensebox_eye.menu.USBMode.hwcdc=Hardware CDC and JTAG +sensebox_eye.menu.USBMode.hwcdc.build.usb_mode=1 + +sensebox_eye.menu.CDCOnBoot.default=Enabled +sensebox_eye.menu.CDCOnBoot.default.build.cdc_on_boot=1 +sensebox_eye.menu.CDCOnBoot.cdc=Disabled +sensebox_eye.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +sensebox_eye.menu.MSCOnBoot.default=Enabled (Requires USB-OTG Mode) +sensebox_eye.menu.MSCOnBoot.default.build.msc_on_boot=1 +sensebox_eye.menu.MSCOnBoot.msc=Disabled +sensebox_eye.menu.MSCOnBoot.msc.build.msc_on_boot=0 + +sensebox_eye.menu.DFUOnBoot.default=Disabled +sensebox_eye.menu.DFUOnBoot.default.build.dfu_on_boot=0 +sensebox_eye.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +sensebox_eye.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +sensebox_eye.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +sensebox_eye.menu.UploadMode.cdc.upload.use_1200bps_touch=true +sensebox_eye.menu.UploadMode.cdc.upload.wait_for_upload_port=true +sensebox_eye.menu.UploadMode.default=UART0 / Hardware CDC +sensebox_eye.menu.UploadMode.default.upload.use_1200bps_touch=false +sensebox_eye.menu.UploadMode.default.upload.wait_for_upload_port=false + +sensebox_eye.menu.PartitionScheme.tinyuf2=TinyUF2 Compatibility (2MB APP/12MB FFAT) +sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader_tinyuf2 +sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions-16MB-tinyuf2 +sensebox_eye.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152 +sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" 0x210000 "{runtime.platform.path}/variants/{build.variant}/APOTA.bin" +sensebox_eye.menu.PartitionScheme.default_16MB=Default (6.25MB APP/3.43MB SPIFFS) +sensebox_eye.menu.PartitionScheme.default_16MB.build.partitions=default_16MB +sensebox_eye.menu.PartitionScheme.default_16MB.upload.maximum_size=6553600 +sensebox_eye.menu.PartitionScheme.large_spiffs=Large SPIFFS (4.5MB APP/6.93MB SPIFFS) +sensebox_eye.menu.PartitionScheme.large_spiffs.build.partitions=large_spiffs_16MB +sensebox_eye.menu.PartitionScheme.large_spiffs.upload.maximum_size=4718592 +sensebox_eye.menu.PartitionScheme.app3M_fat9M_16MB=FFAT (3MB APP/9MB FATFS) +sensebox_eye.menu.PartitionScheme.app3M_fat9M_16MB.build.partitions=app3M_fat9M_16MB +sensebox_eye.menu.PartitionScheme.app3M_fat9M_16MB.upload.maximum_size=3145728 +sensebox_eye.menu.PartitionScheme.fatflash=Large FFAT (2MB APP/12.5MB FATFS) +sensebox_eye.menu.PartitionScheme.fatflash.build.partitions=ffat +sensebox_eye.menu.PartitionScheme.fatflash.upload.maximum_size=2097152 +sensebox_eye.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP) +sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp +sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144 + +sensebox_eye.menu.CPUFreq.240=240MHz (WiFi) +sensebox_eye.menu.CPUFreq.240.build.f_cpu=240000000L +sensebox_eye.menu.CPUFreq.160=160MHz (WiFi) +sensebox_eye.menu.CPUFreq.160.build.f_cpu=160000000L +sensebox_eye.menu.CPUFreq.80=80MHz (WiFi) +sensebox_eye.menu.CPUFreq.80.build.f_cpu=80000000L +sensebox_eye.menu.CPUFreq.40=40MHz +sensebox_eye.menu.CPUFreq.40.build.f_cpu=40000000L +sensebox_eye.menu.CPUFreq.20=20MHz +sensebox_eye.menu.CPUFreq.20.build.f_cpu=20000000L +sensebox_eye.menu.CPUFreq.10=10MHz +sensebox_eye.menu.CPUFreq.10.build.f_cpu=10000000L + +sensebox_eye.menu.UploadSpeed.921600=921600 +sensebox_eye.menu.UploadSpeed.921600.upload.speed=921600 +sensebox_eye.menu.UploadSpeed.115200=115200 +sensebox_eye.menu.UploadSpeed.115200.upload.speed=115200 +sensebox_eye.menu.UploadSpeed.256000.windows=256000 +sensebox_eye.menu.UploadSpeed.256000.upload.speed=256000 +sensebox_eye.menu.UploadSpeed.230400.windows.upload.speed=256000 +sensebox_eye.menu.UploadSpeed.230400=230400 +sensebox_eye.menu.UploadSpeed.230400.upload.speed=230400 +sensebox_eye.menu.UploadSpeed.460800.linux=460800 +sensebox_eye.menu.UploadSpeed.460800.macosx=460800 +sensebox_eye.menu.UploadSpeed.460800.upload.speed=460800 +sensebox_eye.menu.UploadSpeed.512000.windows=512000 +sensebox_eye.menu.UploadSpeed.512000.upload.speed=512000 + +sensebox_eye.menu.DebugLevel.none=None +sensebox_eye.menu.DebugLevel.none.build.code_debug=0 +sensebox_eye.menu.DebugLevel.error=Error +sensebox_eye.menu.DebugLevel.error.build.code_debug=1 +sensebox_eye.menu.DebugLevel.warn=Warn +sensebox_eye.menu.DebugLevel.warn.build.code_debug=2 +sensebox_eye.menu.DebugLevel.info=Info +sensebox_eye.menu.DebugLevel.info.build.code_debug=3 +sensebox_eye.menu.DebugLevel.debug=Debug +sensebox_eye.menu.DebugLevel.debug.build.code_debug=4 +sensebox_eye.menu.DebugLevel.verbose=Verbose +sensebox_eye.menu.DebugLevel.verbose.build.code_debug=5 + +sensebox_eye.menu.EraseFlash.none=Disabled +sensebox_eye.menu.EraseFlash.none.upload.erase_cmd= +sensebox_eye.menu.EraseFlash.all=Enabled +sensebox_eye.menu.EraseFlash.all.upload.erase_cmd=-e + ############################################################## nano_nora.name=Arduino Nano ESP32 @@ -51383,3 +52126,821 @@ fed4.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR fed4.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote ############################################################## + +# FoBE Quill ESP32S3 Mesh FH4R2 + +fobe_quill_esp32s3_mesh.name=FoBE Quill ESP32S3 Mesh +fobe_quill_esp32s3_mesh.vid.0=0x303a +fobe_quill_esp32s3_mesh.pid.0=0x82f4 +fobe_quill_esp32s3_mesh.vid.1=0x303a +fobe_quill_esp32s3_mesh.vid.1=0x82f5 +fobe_quill_esp32s3_mesh.vid.2=0x303a +fobe_quill_esp32s3_mesh.pid.2=0x82f6 + +fobe_quill_esp32s3_mesh.bootloader.tool=esptool_py +fobe_quill_esp32s3_mesh.bootloader.tool.default=esptool_py + +fobe_quill_esp32s3_mesh.upload.tool=esptool_py +fobe_quill_esp32s3_mesh.upload.tool.default=esptool_py +fobe_quill_esp32s3_mesh.upload.tool.network=esp_ota + +fobe_quill_esp32s3_mesh.upload.maximum_size=1310720 +fobe_quill_esp32s3_mesh.upload.maximum_data_size=327680 +fobe_quill_esp32s3_mesh.upload.flags= +fobe_quill_esp32s3_mesh.upload.extra_flags= +fobe_quill_esp32s3_mesh.upload.use_1200bps_touch=true +fobe_quill_esp32s3_mesh.upload.wait_for_upload_port=true + +fobe_quill_esp32s3_mesh.serial.disableDTR=false +fobe_quill_esp32s3_mesh.serial.disableRTS=false + +fobe_quill_esp32s3_mesh.build.tarch=xtensa +fobe_quill_esp32s3_mesh.build.bootloader_addr=0x0 +fobe_quill_esp32s3_mesh.build.target=esp32s3 +fobe_quill_esp32s3_mesh.build.mcu=esp32s3 +fobe_quill_esp32s3_mesh.build.core=esp32 +fobe_quill_esp32s3_mesh.build.variant=fobe_quill_esp32s3_mesh +fobe_quill_esp32s3_mesh.build.board=FOBE_QUILL_ESP32S3_MESH + +fobe_quill_esp32s3_mesh.build.usb_mode=0 +fobe_quill_esp32s3_mesh.build.cdc_on_boot=1 +fobe_quill_esp32s3_mesh.build.msc_on_boot=0 +fobe_quill_esp32s3_mesh.build.dfu_on_boot=0 +fobe_quill_esp32s3_mesh.build.f_cpu=240000000L +fobe_quill_esp32s3_mesh.build.flash_size=4MB +fobe_quill_esp32s3_mesh.build.flash_freq=80m +fobe_quill_esp32s3_mesh.build.flash_mode=qio +fobe_quill_esp32s3_mesh.build.boot=qio +fobe_quill_esp32s3_mesh.build.boot_freq=80m +fobe_quill_esp32s3_mesh.build.partitions=ffat +fobe_quill_esp32s3_mesh.build.defines= +fobe_quill_esp32s3_mesh.build.loop_core= +fobe_quill_esp32s3_mesh.build.event_core= +fobe_quill_esp32s3_mesh.build.psram_type=qspi +fobe_quill_esp32s3_mesh.build.memory_type={build.boot}_{build.psram_type} + +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.default=Disabled +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.default.build.copy_jtag_files=0 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin=Integrated USB JTAG +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external=FTDI Adapter +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.external.build.copy_jtag_files=1 +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge=ESP USB Bridge +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +fobe_quill_esp32s3_mesh.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled=Disabled +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled.build.defines= +fobe_quill_esp32s3_mesh.menu.PSRAM.disabled.build.psram_type=qspi +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled=QSPI PSRAM +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +fobe_quill_esp32s3_mesh.menu.PSRAM.enabled.build.psram_type=qspi + +fobe_quill_esp32s3_mesh.menu.FlashMode.qio=QIO 80MHz +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.flash_mode=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.boot=qio +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.boot_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.qio.build.flash_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.dio=DIO 80MHz +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.flash_mode=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.boot=dio +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.boot_freq=80m +fobe_quill_esp32s3_mesh.menu.FlashMode.dio.build.flash_freq=80m + +fobe_quill_esp32s3_mesh.menu.FlashSize.4M=4MB (32Mb) +fobe_quill_esp32s3_mesh.menu.FlashSize.4M.build.flash_size=4MB + +fobe_quill_esp32s3_mesh.menu.LoopCore.1=Core 1 +fobe_quill_esp32s3_mesh.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +fobe_quill_esp32s3_mesh.menu.LoopCore.0=Core 0 +fobe_quill_esp32s3_mesh.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +fobe_quill_esp32s3_mesh.menu.EventsCore.1=Core 1 +fobe_quill_esp32s3_mesh.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +fobe_quill_esp32s3_mesh.menu.EventsCore.0=Core 0 +fobe_quill_esp32s3_mesh.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +fobe_quill_esp32s3_mesh.menu.USBMode.hwcdc=Hardware CDC and JTAG +fobe_quill_esp32s3_mesh.menu.USBMode.hwcdc.build.usb_mode=1 +fobe_quill_esp32s3_mesh.menu.USBMode.default=USB-OTG (TinyUSB) +fobe_quill_esp32s3_mesh.menu.USBMode.default.build.usb_mode=0 + +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.default=Enabled +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.default.build.cdc_on_boot=1 +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.cdc=Disabled +fobe_quill_esp32s3_mesh.menu.CDCOnBoot.cdc.build.cdc_on_boot=0 + +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.default=Disabled +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.default.build.msc_on_boot=0 +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +fobe_quill_esp32s3_mesh.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.default=Disabled +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.default.build.dfu_on_boot=0 +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +fobe_quill_esp32s3_mesh.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +fobe_quill_esp32s3_mesh.menu.UploadMode.default=UART0 / Hardware CDC +fobe_quill_esp32s3_mesh.menu.UploadMode.default.upload.use_1200bps_touch=false +fobe_quill_esp32s3_mesh.menu.UploadMode.default.upload.wait_for_upload_port=false +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc.upload.use_1200bps_touch=true +fobe_quill_esp32s3_mesh.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2=TinyUF2 4MB (1.3MB APP/960KB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader-tinyuf2 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.build.partitions=tinyuf2-partitions-4MB +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.upload.maximum_size=1441792 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota=TinyUF2 4MB No OTA (2.7MB APP/960KB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.build.custom_bootloader=bootloader-tinyuf2 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.build.partitions=tinyuf2-partitions-4MB-noota +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.upload.maximum_size=2883584 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.tinyuf2_noota.upload.extra_flags=0x2d0000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin" +fobe_quill_esp32s3_mesh.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.default.build.partitions=default +fobe_quill_esp32s3_mesh.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.minimal.build.partitions=minimal +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota.build.partitions=no_ota +fobe_quill_esp32s3_mesh.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +fobe_quill_esp32s3_mesh.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app.build.partitions=huge_app +fobe_quill_esp32s3_mesh.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +fobe_quill_esp32s3_mesh.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +fobe_quill_esp32s3_mesh.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 + +fobe_quill_esp32s3_mesh.menu.CPUFreq.240=240MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.240.build.f_cpu=240000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.160=160MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.160.build.f_cpu=160000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.80=80MHz (WiFi) +fobe_quill_esp32s3_mesh.menu.CPUFreq.80.build.f_cpu=80000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.40=40MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.40.build.f_cpu=40000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.20=20MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.20.build.f_cpu=20000000L +fobe_quill_esp32s3_mesh.menu.CPUFreq.10=10MHz +fobe_quill_esp32s3_mesh.menu.CPUFreq.10.build.f_cpu=10000000L + +fobe_quill_esp32s3_mesh.menu.UploadSpeed.921600=921600 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.921600.upload.speed=921600 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.115200=115200 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.115200.upload.speed=115200 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.256000.windows=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.256000.upload.speed=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400.windows.upload.speed=256000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400=230400 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.230400.upload.speed=230400 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.linux=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.macosx=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.460800.upload.speed=460800 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.512000.windows=512000 +fobe_quill_esp32s3_mesh.menu.UploadSpeed.512000.upload.speed=512000 + +fobe_quill_esp32s3_mesh.menu.DebugLevel.none=None +fobe_quill_esp32s3_mesh.menu.DebugLevel.none.build.code_debug=0 +fobe_quill_esp32s3_mesh.menu.DebugLevel.error=Error +fobe_quill_esp32s3_mesh.menu.DebugLevel.error.build.code_debug=1 +fobe_quill_esp32s3_mesh.menu.DebugLevel.warn=Warn +fobe_quill_esp32s3_mesh.menu.DebugLevel.warn.build.code_debug=2 +fobe_quill_esp32s3_mesh.menu.DebugLevel.info=Info +fobe_quill_esp32s3_mesh.menu.DebugLevel.info.build.code_debug=3 +fobe_quill_esp32s3_mesh.menu.DebugLevel.debug=Debug +fobe_quill_esp32s3_mesh.menu.DebugLevel.debug.build.code_debug=4 +fobe_quill_esp32s3_mesh.menu.DebugLevel.verbose=Verbose +fobe_quill_esp32s3_mesh.menu.DebugLevel.verbose.build.code_debug=5 + +fobe_quill_esp32s3_mesh.menu.EraseFlash.none=Disabled +fobe_quill_esp32s3_mesh.menu.EraseFlash.none.upload.erase_cmd= +fobe_quill_esp32s3_mesh.menu.EraseFlash.all=Enabled +fobe_quill_esp32s3_mesh.menu.EraseFlash.all.upload.erase_cmd=-e + +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default=Disabled +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default.build.zigbee_mode= +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.default.build.zigbee_libs= +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +fobe_quill_esp32s3_mesh.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +twinaiot.name=Twin AIoT Module + +twinaiot.bootloader.tool=esptool_py +twinaiot.bootloader.tool.default=esptool_py + +twinaiot.upload.tool=esptool_py +twinaiot.upload.tool.default=esptool_py +twinaiot.upload.tool.network=esp_ota + +twinaiot.upload.maximum_size=1310720 +twinaiot.upload.maximum_data_size=327680 +twinaiot.upload.use_1200bps_touch=false +twinaiot.upload.wait_for_upload_port=false + +twinaiot.serial.disableDTR=false +twinaiot.serial.disableRTS=false + +twinaiot.build.tarch=xtensa +twinaiot.build.bootloader_addr=0x0 +twinaiot.build.target=esp32s3 +twinaiot.build.mcu=esp32s3 +twinaiot.build.core=esp32 +twinaiot.build.variant=twinaiot +twinaiot.build.board=TWIN_AIOT + +twinaiot.build.usb_mode=1 +twinaiot.build.cdc_on_boot=0 +twinaiot.build.msc_on_boot=0 +twinaiot.build.dfu_on_boot=0 +twinaiot.build.f_cpu=240000000L +twinaiot.build.flash_size=4MB +twinaiot.build.flash_freq=80m +twinaiot.build.flash_mode=dio +twinaiot.build.boot=qio +twinaiot.build.boot_freq=80m +twinaiot.build.partitions=default +twinaiot.build.defines= +twinaiot.build.loop_core= +twinaiot.build.event_core= +twinaiot.build.psram_type=qspi +twinaiot.build.memory_type={build.boot}_{build.psram_type} + +twinaiot.menu.JTAGAdapter.default=Disabled +twinaiot.menu.JTAGAdapter.default.build.copy_jtag_files=0 +twinaiot.menu.JTAGAdapter.builtin=Integrated USB JTAG +twinaiot.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +twinaiot.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +twinaiot.menu.JTAGAdapter.external=FTDI Adapter +twinaiot.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +twinaiot.menu.JTAGAdapter.external.build.copy_jtag_files=1 +twinaiot.menu.JTAGAdapter.bridge=ESP USB Bridge +twinaiot.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +twinaiot.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +twinaiot.menu.PSRAM.disabled=Disabled +twinaiot.menu.PSRAM.disabled.build.defines= +twinaiot.menu.PSRAM.disabled.build.psram_type=qspi +twinaiot.menu.PSRAM.enabled=QSPI PSRAM +twinaiot.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +twinaiot.menu.PSRAM.enabled.build.psram_type=qspi + +twinaiot.menu.FlashMode.qio=QIO 80MHz +twinaiot.menu.FlashMode.qio.build.flash_mode=dio +twinaiot.menu.FlashMode.qio.build.boot=qio +twinaiot.menu.FlashMode.qio.build.boot_freq=80m +twinaiot.menu.FlashMode.qio.build.flash_freq=80m +twinaiot.menu.FlashMode.qio120=QIO 120MHz +twinaiot.menu.FlashMode.qio120.build.flash_mode=dio +twinaiot.menu.FlashMode.qio120.build.boot=qio +twinaiot.menu.FlashMode.qio120.build.boot_freq=120m +twinaiot.menu.FlashMode.qio120.build.flash_freq=80m +twinaiot.menu.FlashMode.dio=DIO 80MHz +twinaiot.menu.FlashMode.dio.build.flash_mode=dio +twinaiot.menu.FlashMode.dio.build.boot=dio +twinaiot.menu.FlashMode.dio.build.boot_freq=80m +twinaiot.menu.FlashMode.dio.build.flash_freq=80m + + +twinaiot.menu.FlashSize.4M=4MB (32Mb) +twinaiot.menu.FlashSize.4M.build.flash_size=4MB + +twinaiot.menu.CPUFreq.240=240MHz (WiFi) +twinaiot.menu.CPUFreq.240.build.f_cpu=240000000L +twinaiot.menu.CPUFreq.160=160MHz (WiFi) +twinaiot.menu.CPUFreq.160.build.f_cpu=160000000L +twinaiot.menu.CPUFreq.80=80MHz (WiFi) +twinaiot.menu.CPUFreq.80.build.f_cpu=80000000L +twinaiot.menu.CPUFreq.40=40MHz +twinaiot.menu.CPUFreq.40.build.f_cpu=40000000L +twinaiot.menu.CPUFreq.20=20MHz +twinaiot.menu.CPUFreq.20.build.f_cpu=20000000L +twinaiot.menu.CPUFreq.10=10MHz +twinaiot.menu.CPUFreq.10.build.f_cpu=10000000L + +twinaiot.menu.UploadSpeed.921600=921600 +twinaiot.menu.UploadSpeed.921600.upload.speed=921600 +twinaiot.menu.UploadSpeed.115200=115200 +twinaiot.menu.UploadSpeed.115200.upload.speed=115200 +twinaiot.menu.UploadSpeed.256000.windows=256000 +twinaiot.menu.UploadSpeed.256000.upload.speed=256000 +twinaiot.menu.UploadSpeed.230400.windows.upload.speed=256000 +twinaiot.menu.UploadSpeed.230400=230400 +twinaiot.menu.UploadSpeed.230400.upload.speed=230400 +twinaiot.menu.UploadSpeed.460800.linux=460800 +twinaiot.menu.UploadSpeed.460800.macosx=460800 +twinaiot.menu.UploadSpeed.460800.upload.speed=460800 +twinaiot.menu.UploadSpeed.512000.windows=512000 +twinaiot.menu.UploadSpeed.512000.upload.speed=512000 + +twinaiot.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +twinaiot.menu.PartitionScheme.default.build.partitions=default +twinaiot.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +twinaiot.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +twinaiot.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +twinaiot.menu.PartitionScheme.minimal.build.partitions=minimal +twinaiot.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +twinaiot.menu.PartitionScheme.no_fs.build.partitions=no_fs +twinaiot.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +twinaiot.menu.PartitionScheme.no_ota.build.partitions=no_ota +twinaiot.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +twinaiot.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +twinaiot.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +twinaiot.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +twinaiot.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +twinaiot.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +twinaiot.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +twinaiot.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +twinaiot.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +twinaiot.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +twinaiot.menu.PartitionScheme.huge_app.build.partitions=huge_app +twinaiot.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +twinaiot.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +twinaiot.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +twinaiot.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +twinaiot.menu.PartitionScheme.rainmaker=RainMaker 4MB +twinaiot.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +twinaiot.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +twinaiot.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +twinaiot.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +twinaiot.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +twinaiot.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +twinaiot.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +twinaiot.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +twinaiot.menu.PartitionScheme.custom=Custom +twinaiot.menu.PartitionScheme.custom.build.partitions= +twinaiot.menu.PartitionScheme.custom.upload.maximum_size=16777216 + +twinaiot.menu.LoopCore.1=Core 1 +twinaiot.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +twinaiot.menu.LoopCore.0=Core 0 +twinaiot.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +twinaiot.menu.EventsCore.1=Core 1 +twinaiot.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +twinaiot.menu.EventsCore.0=Core 0 +twinaiot.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +twinaiot.menu.USBMode.hwcdc=Hardware CDC and JTAG +twinaiot.menu.USBMode.hwcdc.build.usb_mode=1 +twinaiot.menu.USBMode.default=USB-OTG (TinyUSB) +twinaiot.menu.USBMode.default.build.usb_mode=0 + +twinaiot.menu.CDCOnBoot.default=Disabled +twinaiot.menu.CDCOnBoot.default.build.cdc_on_boot=0 +twinaiot.menu.CDCOnBoot.cdc=Enabled +twinaiot.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +twinaiot.menu.MSCOnBoot.default=Disabled +twinaiot.menu.MSCOnBoot.default.build.msc_on_boot=0 +twinaiot.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +twinaiot.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +twinaiot.menu.DFUOnBoot.default=Disabled +twinaiot.menu.DFUOnBoot.default.build.dfu_on_boot=0 +twinaiot.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +twinaiot.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +twinaiot.menu.UploadMode.default=UART0 / Hardware CDC +twinaiot.menu.UploadMode.default.upload.use_1200bps_touch=false +twinaiot.menu.UploadMode.default.upload.wait_for_upload_port=false +twinaiot.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +twinaiot.menu.UploadMode.cdc.upload.use_1200bps_touch=true +twinaiot.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +twinaiot.menu.DebugLevel.none=None +twinaiot.menu.DebugLevel.none.build.code_debug=0 +twinaiot.menu.DebugLevel.error=Error +twinaiot.menu.DebugLevel.error.build.code_debug=1 +twinaiot.menu.DebugLevel.warn=Warn +twinaiot.menu.DebugLevel.warn.build.code_debug=2 +twinaiot.menu.DebugLevel.info=Info +twinaiot.menu.DebugLevel.info.build.code_debug=3 +twinaiot.menu.DebugLevel.debug=Debug +twinaiot.menu.DebugLevel.debug.build.code_debug=4 +twinaiot.menu.DebugLevel.verbose=Verbose +twinaiot.menu.DebugLevel.verbose.build.code_debug=5 + +twinaiot.menu.EraseFlash.none=Disabled +twinaiot.menu.EraseFlash.none.upload.erase_cmd= +twinaiot.menu.EraseFlash.all=Enabled +twinaiot.menu.EraseFlash.all.upload.erase_cmd=-e + +twinaiot.menu.ZigbeeMode.default=Disabled +twinaiot.menu.ZigbeeMode.default.build.zigbee_mode= +twinaiot.menu.ZigbeeMode.default.build.zigbee_libs= +twinaiot.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +twinaiot.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +twinaiot.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## + +esp32p4_4ds_mipi.name=4D Systems ESP32-P4 MIPI Displays + +esp32p4_4ds_mipi.bootloader.tool=esptool_py +esp32p4_4ds_mipi.bootloader.tool.default=esptool_py + +esp32p4_4ds_mipi.upload.tool=esptool_py +esp32p4_4ds_mipi.upload.tool.default=esptool_py +esp32p4_4ds_mipi.upload.tool.network=esp_ota + +esp32p4_4ds_mipi.upload.maximum_size=1310720 +esp32p4_4ds_mipi.upload.maximum_data_size=327680 +esp32p4_4ds_mipi.upload.flags= +esp32p4_4ds_mipi.upload.extra_flags= +esp32p4_4ds_mipi.upload.use_1200bps_touch=false +esp32p4_4ds_mipi.upload.wait_for_upload_port=false + +esp32p4_4ds_mipi.serial.disableDTR=false +esp32p4_4ds_mipi.serial.disableRTS=false + +esp32p4_4ds_mipi.build.tarch=riscv32 +esp32p4_4ds_mipi.build.target=esp +esp32p4_4ds_mipi.build.mcu=esp32p4 +esp32p4_4ds_mipi.build.core=esp32 +esp32p4_4ds_mipi.build.variant=esp32p4_4ds_mipi +esp32p4_4ds_mipi.build.board=ESP32P4_4DS_MIPI +esp32p4_4ds_mipi.build.bootloader_addr=0x2000 + +esp32p4_4ds_mipi.build.usb_mode=0 +esp32p4_4ds_mipi.build.cdc_on_boot=0 +esp32p4_4ds_mipi.build.msc_on_boot=0 +esp32p4_4ds_mipi.build.dfu_on_boot=0 +esp32p4_4ds_mipi.build.f_cpu=360000000L +esp32p4_4ds_mipi.build.flash_size=32MB +esp32p4_4ds_mipi.build.flash_freq=80m +esp32p4_4ds_mipi.build.img_freq=80m +esp32p4_4ds_mipi.build.flash_mode=qio +esp32p4_4ds_mipi.build.boot=qio +esp32p4_4ds_mipi.build.partitions=app5M_fat24M_32MB + +## IDE 2.0 Seems to not update the value +esp32p4_4ds_mipi.menu.JTAGAdapter.default=Disabled +esp32p4_4ds_mipi.menu.JTAGAdapter.default.build.copy_jtag_files=0 +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin=Integrated USB JTAG +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin.build.openocdscript=esp32p4-builtin.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +esp32p4_4ds_mipi.menu.JTAGAdapter.external=FTDI Adapter +esp32p4_4ds_mipi.menu.JTAGAdapter.external.build.openocdscript=esp32p4-ftdi.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.external.build.copy_jtag_files=1 +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge=ESP USB Bridge +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge.build.openocdscript=esp32p4-bridge.cfg +esp32p4_4ds_mipi.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +esp32p4_4ds_mipi.menu.USBMode.default=USB-OTG (TinyUSB) +esp32p4_4ds_mipi.menu.USBMode.default.build.usb_mode=0 +esp32p4_4ds_mipi.menu.USBMode.hwcdc=Hardware CDC and JTAG +esp32p4_4ds_mipi.menu.USBMode.hwcdc.build.usb_mode=1 + +esp32p4_4ds_mipi.menu.CDCOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.CDCOnBoot.default.build.cdc_on_boot=0 +esp32p4_4ds_mipi.menu.CDCOnBoot.cdc=Enabled +esp32p4_4ds_mipi.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 + +esp32p4_4ds_mipi.menu.MSCOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.MSCOnBoot.default.build.msc_on_boot=0 +esp32p4_4ds_mipi.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +esp32p4_4ds_mipi.menu.DFUOnBoot.default=Disabled +esp32p4_4ds_mipi.menu.DFUOnBoot.default.build.dfu_on_boot=0 +esp32p4_4ds_mipi.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +esp32p4_4ds_mipi.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +esp32p4_4ds_mipi.menu.UploadMode.default=UART0 / Hardware CDC +esp32p4_4ds_mipi.menu.UploadMode.default.upload.use_1200bps_touch=false +esp32p4_4ds_mipi.menu.UploadMode.default.upload.wait_for_upload_port=false +esp32p4_4ds_mipi.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +esp32p4_4ds_mipi.menu.UploadMode.cdc.upload.use_1200bps_touch=true +esp32p4_4ds_mipi.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB=32M Flash (4.8MB APP/22MB FATFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB.build.partitions=large_fat_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_fat24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB=32M Flash (4.8MB APP/22MB LittleFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB.build.partitions=large_littlefs_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app5M_little24M_32MB.upload.maximum_size=4718592 +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB=32M Flash (13MB APP/6.75MB SPIFFS) +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB.build.partitions=default_32MB +esp32p4_4ds_mipi.menu.PartitionScheme.app13M_data7M_32MB.upload.maximum_size=13107200 + +## From https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/kconfig.html#config-esp-default-cpu-freq-mhz +esp32p4_4ds_mipi.menu.CPUFreq.360=360MHz +esp32p4_4ds_mipi.menu.CPUFreq.360.build.f_cpu=360000000L +esp32p4_4ds_mipi.menu.CPUFreq.40=40MHz +esp32p4_4ds_mipi.menu.CPUFreq.40.build.f_cpu=40000000L + +esp32p4_4ds_mipi.menu.FlashSize.16M=16MB (128Mb) +esp32p4_4ds_mipi.menu.FlashSize.16M.build.flash_size=16MB +esp32p4_4ds_mipi.menu.FlashSize.32M=32MB (256Mb) +esp32p4_4ds_mipi.menu.FlashSize.32M.build.flash_size=32MB + +esp32p4_4ds_mipi.menu.UploadSpeed.921600=921600 +esp32p4_4ds_mipi.menu.UploadSpeed.921600.upload.speed=921600 +esp32p4_4ds_mipi.menu.UploadSpeed.115200=115200 +esp32p4_4ds_mipi.menu.UploadSpeed.115200.upload.speed=115200 +esp32p4_4ds_mipi.menu.UploadSpeed.256000.windows=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.256000.upload.speed=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.230400.windows.upload.speed=256000 +esp32p4_4ds_mipi.menu.UploadSpeed.230400=230400 +esp32p4_4ds_mipi.menu.UploadSpeed.230400.upload.speed=230400 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.linux=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.macosx=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.460800.upload.speed=460800 +esp32p4_4ds_mipi.menu.UploadSpeed.512000.windows=512000 +esp32p4_4ds_mipi.menu.UploadSpeed.512000.upload.speed=512000 + +esp32p4_4ds_mipi.menu.DebugLevel.none=None +esp32p4_4ds_mipi.menu.DebugLevel.none.build.code_debug=0 +esp32p4_4ds_mipi.menu.DebugLevel.error=Error +esp32p4_4ds_mipi.menu.DebugLevel.error.build.code_debug=1 +esp32p4_4ds_mipi.menu.DebugLevel.warn=Warn +esp32p4_4ds_mipi.menu.DebugLevel.warn.build.code_debug=2 +esp32p4_4ds_mipi.menu.DebugLevel.info=Info +esp32p4_4ds_mipi.menu.DebugLevel.info.build.code_debug=3 +esp32p4_4ds_mipi.menu.DebugLevel.debug=Debug +esp32p4_4ds_mipi.menu.DebugLevel.debug.build.code_debug=4 +esp32p4_4ds_mipi.menu.DebugLevel.verbose=Verbose +esp32p4_4ds_mipi.menu.DebugLevel.verbose.build.code_debug=5 + +esp32p4_4ds_mipi.menu.EraseFlash.none=Disabled +esp32p4_4ds_mipi.menu.EraseFlash.none.upload.erase_cmd= +esp32p4_4ds_mipi.menu.EraseFlash.all=Enabled +esp32p4_4ds_mipi.menu.EraseFlash.all.upload.erase_cmd=-e + +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70=ESP32-P4-70 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70.build.DisplayModel=ESP32P4_70 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct=ESP32-P4-70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct.build.DisplayModel=ESP32P4_70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct_clb=ESP32-P4-70CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_70ct_clb.build.DisplayModel=ESP32P4_70CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80=ESP32-P4-80 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80.build.DisplayModel=ESP32P4_80 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct=ESP32-P4-80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct.build.DisplayModel=ESP32P4_80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct_clb=ESP32-P4-80CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_80ct_clb.build.DisplayModel=ESP32P4_80CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90=ESP32-P4-90 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90.build.DisplayModel=ESP32P4_90 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct=ESP32-P4-90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct.build.DisplayModel=ESP32P4_90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct_clb=ESP32-P4-90CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_90ct_clb.build.DisplayModel=ESP32P4_90CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101=ESP32-P4-101 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101.build.DisplayModel=ESP32P4_101 +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct=ESP32-P4-101CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct.build.DisplayModel=ESP32P4_101CT +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct_clb=ESP32-P4-101CT-CLB +esp32p4_4ds_mipi.menu.DisplayModel.esp32p4_101ct_clb.build.DisplayModel=ESP32P4_101CT + +esp32p4_4ds_mipi.build.defines=-DBOARD_HAS_PSRAM -D{build.board} -D{build.DisplayModel} + +############################################################## + +# Axiometa PIXIE M1 - Based on ESP32-S3-Mini-N4R2 +# 4MB Quad SPI Flash, 2MB Quad SPI PSRAM + +axiometa_pixie_m1.name=Axiometa PIXIE M1 + +axiometa_pixie_m1.bootloader.tool=esptool_py +axiometa_pixie_m1.bootloader.tool.default=esptool_py + +axiometa_pixie_m1.upload.tool=esptool_py +axiometa_pixie_m1.upload.tool.default=esptool_py +axiometa_pixie_m1.upload.tool.network=esp_ota + +axiometa_pixie_m1.upload.maximum_size=1310720 +axiometa_pixie_m1.upload.maximum_data_size=327680 +axiometa_pixie_m1.upload.flags= +axiometa_pixie_m1.upload.extra_flags= +axiometa_pixie_m1.upload.use_1200bps_touch=false +axiometa_pixie_m1.upload.wait_for_upload_port=false + +axiometa_pixie_m1.serial.disableDTR=false +axiometa_pixie_m1.serial.disableRTS=false + +axiometa_pixie_m1.build.tarch=xtensa +axiometa_pixie_m1.build.bootloader_addr=0x0 +axiometa_pixie_m1.build.target=esp32s3 +axiometa_pixie_m1.build.mcu=esp32s3 +axiometa_pixie_m1.build.core=esp32 +axiometa_pixie_m1.build.variant=axiometa_pixie_m1 +axiometa_pixie_m1.build.board=AXIOMETA_PIXIE_M1 + +# Hardware Configuration (ESP32-S3-Mini-N4R2) +axiometa_pixie_m1.build.usb_mode=1 +axiometa_pixie_m1.build.cdc_on_boot=1 +axiometa_pixie_m1.build.msc_on_boot=0 +axiometa_pixie_m1.build.dfu_on_boot=0 +axiometa_pixie_m1.build.f_cpu=240000000L +axiometa_pixie_m1.build.flash_size=4MB +axiometa_pixie_m1.build.flash_freq=80m +axiometa_pixie_m1.build.flash_mode=dio +axiometa_pixie_m1.build.boot=qio +axiometa_pixie_m1.build.boot_freq=80m +axiometa_pixie_m1.build.partitions=default +axiometa_pixie_m1.build.defines=-DBOARD_HAS_PSRAM +axiometa_pixie_m1.build.loop_core= +axiometa_pixie_m1.build.event_core= +axiometa_pixie_m1.build.psram_type=qspi +axiometa_pixie_m1.build.memory_type={build.boot}_{build.psram_type} + +## JTAG Adapter - N4R2 Compatible +axiometa_pixie_m1.menu.JTAGAdapter.default=Disabled +axiometa_pixie_m1.menu.JTAGAdapter.default.build.copy_jtag_files=0 +axiometa_pixie_m1.menu.JTAGAdapter.builtin=Integrated USB JTAG +axiometa_pixie_m1.menu.JTAGAdapter.builtin.build.openocdscript=esp32s3-builtin.cfg +axiometa_pixie_m1.menu.JTAGAdapter.builtin.build.copy_jtag_files=1 +axiometa_pixie_m1.menu.JTAGAdapter.external=FTDI Adapter +axiometa_pixie_m1.menu.JTAGAdapter.external.build.openocdscript=esp32s3-ftdi.cfg +axiometa_pixie_m1.menu.JTAGAdapter.external.build.copy_jtag_files=1 +axiometa_pixie_m1.menu.JTAGAdapter.bridge=ESP USB Bridge +axiometa_pixie_m1.menu.JTAGAdapter.bridge.build.openocdscript=esp32s3-bridge.cfg +axiometa_pixie_m1.menu.JTAGAdapter.bridge.build.copy_jtag_files=1 + +## PSRAM - N4R2 has 2MB QSPI PSRAM +axiometa_pixie_m1.menu.PSRAM.enabled=QSPI PSRAM (Enabled) +axiometa_pixie_m1.menu.PSRAM.enabled.build.defines=-DBOARD_HAS_PSRAM +axiometa_pixie_m1.menu.PSRAM.enabled.build.psram_type=qspi +axiometa_pixie_m1.menu.PSRAM.disabled=Disabled +axiometa_pixie_m1.menu.PSRAM.disabled.build.defines= +axiometa_pixie_m1.menu.PSRAM.disabled.build.psram_type=qspi + +## Flash Mode - N4R2 Compatible (QSPI Flash) +axiometa_pixie_m1.menu.FlashMode.qio=QIO 80MHz +axiometa_pixie_m1.menu.FlashMode.qio.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.qio.build.boot=qio +axiometa_pixie_m1.menu.FlashMode.qio.build.boot_freq=80m +axiometa_pixie_m1.menu.FlashMode.qio.build.flash_freq=80m +axiometa_pixie_m1.menu.FlashMode.qio120=QIO 120MHz +axiometa_pixie_m1.menu.FlashMode.qio120.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.qio120.build.boot=qio +axiometa_pixie_m1.menu.FlashMode.qio120.build.boot_freq=120m +axiometa_pixie_m1.menu.FlashMode.qio120.build.flash_freq=80m +axiometa_pixie_m1.menu.FlashMode.dio=DIO 80MHz +axiometa_pixie_m1.menu.FlashMode.dio.build.flash_mode=dio +axiometa_pixie_m1.menu.FlashMode.dio.build.boot=dio +axiometa_pixie_m1.menu.FlashMode.dio.build.boot_freq=80m +axiometa_pixie_m1.menu.FlashMode.dio.build.flash_freq=80m + +## CPU Core Assignment +axiometa_pixie_m1.menu.LoopCore.1=Core 1 +axiometa_pixie_m1.menu.LoopCore.1.build.loop_core=-DARDUINO_RUNNING_CORE=1 +axiometa_pixie_m1.menu.LoopCore.0=Core 0 +axiometa_pixie_m1.menu.LoopCore.0.build.loop_core=-DARDUINO_RUNNING_CORE=0 + +axiometa_pixie_m1.menu.EventsCore.1=Core 1 +axiometa_pixie_m1.menu.EventsCore.1.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=1 +axiometa_pixie_m1.menu.EventsCore.0=Core 0 +axiometa_pixie_m1.menu.EventsCore.0.build.event_core=-DARDUINO_EVENT_RUNNING_CORE=0 + +## USB Mode - Both modes work on N4R2 +axiometa_pixie_m1.menu.USBMode.hwcdc=Hardware CDC and JTAG +axiometa_pixie_m1.menu.USBMode.hwcdc.build.usb_mode=1 +axiometa_pixie_m1.menu.USBMode.default=USB-OTG (TinyUSB) +axiometa_pixie_m1.menu.USBMode.default.build.usb_mode=0 + +## CDC On Boot +axiometa_pixie_m1.menu.CDCOnBoot.cdc=Enabled +axiometa_pixie_m1.menu.CDCOnBoot.cdc.build.cdc_on_boot=1 +axiometa_pixie_m1.menu.CDCOnBoot.default=Disabled +axiometa_pixie_m1.menu.CDCOnBoot.default.build.cdc_on_boot=0 + +## MSC On Boot (Only works with USB-OTG mode) +axiometa_pixie_m1.menu.MSCOnBoot.default=Disabled +axiometa_pixie_m1.menu.MSCOnBoot.default.build.msc_on_boot=0 +axiometa_pixie_m1.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode) +axiometa_pixie_m1.menu.MSCOnBoot.msc.build.msc_on_boot=1 + +## DFU On Boot (Only works with USB-OTG mode) +axiometa_pixie_m1.menu.DFUOnBoot.default=Disabled +axiometa_pixie_m1.menu.DFUOnBoot.default.build.dfu_on_boot=0 +axiometa_pixie_m1.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode) +axiometa_pixie_m1.menu.DFUOnBoot.dfu.build.dfu_on_boot=1 + +## Upload Mode +axiometa_pixie_m1.menu.UploadMode.default=UART0 / Hardware CDC +axiometa_pixie_m1.menu.UploadMode.default.upload.use_1200bps_touch=false +axiometa_pixie_m1.menu.UploadMode.default.upload.wait_for_upload_port=false +axiometa_pixie_m1.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB) +axiometa_pixie_m1.menu.UploadMode.cdc.upload.use_1200bps_touch=true +axiometa_pixie_m1.menu.UploadMode.cdc.upload.wait_for_upload_port=true + +## Partition Schemes - 4MB Flash Compatible Only +axiometa_pixie_m1.menu.PartitionScheme.default=Default 4MB with spiffs (1.2MB APP/1.5MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.default.build.partitions=default +axiometa_pixie_m1.menu.PartitionScheme.defaultffat=Default 4MB with ffat (1.2MB APP/1.5MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.defaultffat.build.partitions=default_ffat +axiometa_pixie_m1.menu.PartitionScheme.minimal=Minimal (1.3MB APP/700KB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.minimal.build.partitions=minimal +axiometa_pixie_m1.menu.PartitionScheme.no_fs=No FS 4MB (2MB APP x2) +axiometa_pixie_m1.menu.PartitionScheme.no_fs.build.partitions=no_fs +axiometa_pixie_m1.menu.PartitionScheme.no_fs.upload.maximum_size=2031616 +axiometa_pixie_m1.menu.PartitionScheme.no_ota=No OTA (2MB APP/2MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.no_ota.build.partitions=no_ota +axiometa_pixie_m1.menu.PartitionScheme.no_ota.upload.maximum_size=2097152 +axiometa_pixie_m1.menu.PartitionScheme.noota_3g=No OTA (1MB APP/3MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_3g.build.partitions=noota_3g +axiometa_pixie_m1.menu.PartitionScheme.noota_3g.upload.maximum_size=1048576 +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat=No OTA (2MB APP/2MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat.build.partitions=noota_ffat +axiometa_pixie_m1.menu.PartitionScheme.noota_ffat.upload.maximum_size=2097152 +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat=No OTA (1MB APP/3MB FATFS) +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat.build.partitions=noota_3gffat +axiometa_pixie_m1.menu.PartitionScheme.noota_3gffat.upload.maximum_size=1048576 +axiometa_pixie_m1.menu.PartitionScheme.huge_app=Huge APP (3MB No OTA/1MB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.huge_app.build.partitions=huge_app +axiometa_pixie_m1.menu.PartitionScheme.huge_app.upload.maximum_size=3145728 +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs=Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS) +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs.build.partitions=min_spiffs +axiometa_pixie_m1.menu.PartitionScheme.min_spiffs.upload.maximum_size=1966080 +axiometa_pixie_m1.menu.PartitionScheme.rainmaker=RainMaker 4MB +axiometa_pixie_m1.menu.PartitionScheme.rainmaker.build.partitions=rainmaker +axiometa_pixie_m1.menu.PartitionScheme.rainmaker.upload.maximum_size=1966080 +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB=RainMaker 4MB No OTA +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB.build.partitions=rainmaker_4MB_no_ota +axiometa_pixie_m1.menu.PartitionScheme.rainmaker_4MB.upload.maximum_size=4038656 +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr=Zigbee ZCZR 4MB with spiffs +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr.build.partitions=zigbee_zczr +axiometa_pixie_m1.menu.PartitionScheme.zigbee_zczr.upload.maximum_size=1310720 +axiometa_pixie_m1.menu.PartitionScheme.custom=Custom +axiometa_pixie_m1.menu.PartitionScheme.custom.build.partitions= +axiometa_pixie_m1.menu.PartitionScheme.custom.upload.maximum_size=4194304 + +## CPU Frequency +axiometa_pixie_m1.menu.CPUFreq.240=240MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.240.build.f_cpu=240000000L +axiometa_pixie_m1.menu.CPUFreq.160=160MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.160.build.f_cpu=160000000L +axiometa_pixie_m1.menu.CPUFreq.80=80MHz (WiFi) +axiometa_pixie_m1.menu.CPUFreq.80.build.f_cpu=80000000L +axiometa_pixie_m1.menu.CPUFreq.40=40MHz +axiometa_pixie_m1.menu.CPUFreq.40.build.f_cpu=40000000L +axiometa_pixie_m1.menu.CPUFreq.20=20MHz +axiometa_pixie_m1.menu.CPUFreq.20.build.f_cpu=20000000L +axiometa_pixie_m1.menu.CPUFreq.10=10MHz +axiometa_pixie_m1.menu.CPUFreq.10.build.f_cpu=10000000L + +## Upload Speed +axiometa_pixie_m1.menu.UploadSpeed.921600=921600 +axiometa_pixie_m1.menu.UploadSpeed.921600.upload.speed=921600 +axiometa_pixie_m1.menu.UploadSpeed.115200=115200 +axiometa_pixie_m1.menu.UploadSpeed.115200.upload.speed=115200 +axiometa_pixie_m1.menu.UploadSpeed.256000.windows=256000 +axiometa_pixie_m1.menu.UploadSpeed.256000.upload.speed=256000 +axiometa_pixie_m1.menu.UploadSpeed.230400.windows.upload.speed=256000 +axiometa_pixie_m1.menu.UploadSpeed.230400=230400 +axiometa_pixie_m1.menu.UploadSpeed.230400.upload.speed=230400 +axiometa_pixie_m1.menu.UploadSpeed.460800.linux=460800 +axiometa_pixie_m1.menu.UploadSpeed.460800.macosx=460800 +axiometa_pixie_m1.menu.UploadSpeed.460800.upload.speed=460800 +axiometa_pixie_m1.menu.UploadSpeed.512000.windows=512000 +axiometa_pixie_m1.menu.UploadSpeed.512000.upload.speed=512000 + +## Debug Level +axiometa_pixie_m1.menu.DebugLevel.none=None +axiometa_pixie_m1.menu.DebugLevel.none.build.code_debug=0 +axiometa_pixie_m1.menu.DebugLevel.error=Error +axiometa_pixie_m1.menu.DebugLevel.error.build.code_debug=1 +axiometa_pixie_m1.menu.DebugLevel.warn=Warn +axiometa_pixie_m1.menu.DebugLevel.warn.build.code_debug=2 +axiometa_pixie_m1.menu.DebugLevel.info=Info +axiometa_pixie_m1.menu.DebugLevel.info.build.code_debug=3 +axiometa_pixie_m1.menu.DebugLevel.debug=Debug +axiometa_pixie_m1.menu.DebugLevel.debug.build.code_debug=4 +axiometa_pixie_m1.menu.DebugLevel.verbose=Verbose +axiometa_pixie_m1.menu.DebugLevel.verbose.build.code_debug=5 + +## Erase Flash +axiometa_pixie_m1.menu.EraseFlash.none=Disabled +axiometa_pixie_m1.menu.EraseFlash.none.upload.erase_cmd= +axiometa_pixie_m1.menu.EraseFlash.all=Enabled +axiometa_pixie_m1.menu.EraseFlash.all.upload.erase_cmd=-e + +## Zigbee Mode +axiometa_pixie_m1.menu.ZigbeeMode.default=Disabled +axiometa_pixie_m1.menu.ZigbeeMode.default.build.zigbee_mode= +axiometa_pixie_m1.menu.ZigbeeMode.default.build.zigbee_libs= +axiometa_pixie_m1.menu.ZigbeeMode.zczr=Zigbee ZCZR (coordinator/router) +axiometa_pixie_m1.menu.ZigbeeMode.zczr.build.zigbee_mode=-DZIGBEE_MODE_ZCZR +axiometa_pixie_m1.menu.ZigbeeMode.zczr.build.zigbee_libs=-lesp_zb_api.zczr -lzboss_stack.zczr -lzboss_port.remote + +############################################################## diff --git a/cores/esp32/Arduino.h b/cores/esp32/Arduino.h index 9048249a873..5a5b0c6814c 100644 --- a/cores/esp32/Arduino.h +++ b/cores/esp32/Arduino.h @@ -222,12 +222,36 @@ size_t getArduinoLoopTaskStackSize(void); return sz; \ } +#define ESP32_USB_MIDI_DEFAULT_NAME "TinyUSB MIDI" +/** +* @brief Set the current device name +* 1. Name set via constructor (if any) +* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) +* 3. Default name "TinyUSB MIDI" +* If device name is set as "", it will be ignored +*/ +#define SET_USB_MIDI_DEVICE_NAME(name) \ + const char *getUSBMIDIDefaultDeviceName() { \ + if (!name || strlen(name) == 0) { \ + return ESP32_USB_MIDI_DEFAULT_NAME; \ + } \ + return name; \ + } + bool shouldPrintChipDebugReport(void); #define ENABLE_CHIP_DEBUG_REPORT \ bool shouldPrintChipDebugReport(void) { \ return true; \ } +// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds +// before the sketch would start its execution. It gives the user time to open the Serial Monitor +uint64_t getArduinoSetupWaitTime_ms(void); +#define SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) \ + uint64_t getArduinoSetupWaitTime_ms() { \ + return (time_ms); \ + } + // allows user to bypass esp_spiram_test() bool esp_psram_extram_test(void); #define BYPASS_SPIRAM_TEST(bypass) \ diff --git a/cores/esp32/Esp.cpp b/cores/esp32/Esp.cpp index f911b2d5f20..061404977f9 100644 --- a/cores/esp32/Esp.cpp +++ b/cores/esp32/Esp.cpp @@ -91,39 +91,39 @@ extern "C" { * uint32_t = test = 10_MHz; // --> 10000000 */ -unsigned long long operator"" _kHz(unsigned long long x) { +unsigned long long operator""_kHz(unsigned long long x) { return x * 1000; } -unsigned long long operator"" _MHz(unsigned long long x) { +unsigned long long operator""_MHz(unsigned long long x) { return x * 1000 * 1000; } -unsigned long long operator"" _GHz(unsigned long long x) { +unsigned long long operator""_GHz(unsigned long long x) { return x * 1000 * 1000 * 1000; } -unsigned long long operator"" _kBit(unsigned long long x) { +unsigned long long operator""_kBit(unsigned long long x) { return x * 1024; } -unsigned long long operator"" _MBit(unsigned long long x) { +unsigned long long operator""_MBit(unsigned long long x) { return x * 1024 * 1024; } -unsigned long long operator"" _GBit(unsigned long long x) { +unsigned long long operator""_GBit(unsigned long long x) { return x * 1024 * 1024 * 1024; } -unsigned long long operator"" _kB(unsigned long long x) { +unsigned long long operator""_kB(unsigned long long x) { return x * 1024; } -unsigned long long operator"" _MB(unsigned long long x) { +unsigned long long operator""_MB(unsigned long long x) { return x * 1024 * 1024; } -unsigned long long operator"" _GB(unsigned long long x) { +unsigned long long operator""_GB(unsigned long long x) { return x * 1024 * 1024 * 1024; } @@ -378,7 +378,7 @@ FlashMode_t EspClass::getFlashChipMode(void) { } #endif // if !defined(CONFIG_IDF_TARGET_ESP32P4) -uint32_t EspClass::magicFlashChipSize(uint8_t byte) { +uint32_t EspClass::magicFlashChipSize(uint8_t flashByte) { /* FLASH_SIZES = { "1MB": 0x00, @@ -391,7 +391,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { "128MB": 0x70, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0x0: return (1_MB); // 8 MBit (1MB) case 0x1: return (2_MB); // 16 MBit (2MB) case 0x2: return (4_MB); // 32 MBit (4MB) @@ -405,7 +405,7 @@ uint32_t EspClass::magicFlashChipSize(uint8_t byte) { } } -uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { +uint32_t EspClass::magicFlashChipSpeed(uint8_t flashByte) { #if CONFIG_IDF_TARGET_ESP32C2 /* FLASH_FREQUENCY = { @@ -415,7 +415,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "15m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (60_MHz); case 0x0: return (30_MHz); case 0x1: return (20_MHz); @@ -432,7 +432,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "20m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0x0: return (80_MHz); case 0x2: return (20_MHz); default: // fail? @@ -449,7 +449,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "12m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (48_MHz); case 0x0: return (24_MHz); case 0x1: return (16_MHz); @@ -467,7 +467,7 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { "20m": 0x2, } */ - switch (byte & 0x0F) { + switch (flashByte & 0x0F) { case 0xF: return (80_MHz); case 0x0: return (40_MHz); case 0x1: return (26_MHz); @@ -478,8 +478,8 @@ uint32_t EspClass::magicFlashChipSpeed(uint8_t byte) { #endif } -FlashMode_t EspClass::magicFlashChipMode(uint8_t byte) { - FlashMode_t mode = (FlashMode_t)byte; +FlashMode_t EspClass::magicFlashChipMode(uint8_t flashByte) { + FlashMode_t mode = (FlashMode_t)flashByte; if (mode > FM_SLOW_READ) { mode = FM_UNKNOWN; } diff --git a/cores/esp32/Esp.h b/cores/esp32/Esp.h index 41068fb9e65..0b496c91c1b 100644 --- a/cores/esp32/Esp.h +++ b/cores/esp32/Esp.h @@ -92,9 +92,9 @@ class EspClass { uint32_t getFlashChipSpeed(); FlashMode_t getFlashChipMode(); - uint32_t magicFlashChipSize(uint8_t byte); - uint32_t magicFlashChipSpeed(uint8_t byte); - FlashMode_t magicFlashChipMode(uint8_t byte); + uint32_t magicFlashChipSize(uint8_t flashByte); + uint32_t magicFlashChipSpeed(uint8_t flashByte); + FlashMode_t magicFlashChipMode(uint8_t flashByte); uint32_t getSketchSize(); String getSketchMD5(); diff --git a/cores/esp32/HEXBuilder.cpp b/cores/esp32/HEXBuilder.cpp index 6154f58b384..4298ad65d6a 100644 --- a/cores/esp32/HEXBuilder.cpp +++ b/cores/esp32/HEXBuilder.cpp @@ -17,8 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include +#include "HEXBuilder.h" static uint8_t hex_char_to_byte(uint8_t c) { return (c >= 'a' && c <= 'f') ? (c - ((uint8_t)'a' - 0xa)) diff --git a/cores/esp32/HEXBuilder.h b/cores/esp32/HEXBuilder.h index 0c35fbc1acc..c5b8a8a88f4 100644 --- a/cores/esp32/HEXBuilder.h +++ b/cores/esp32/HEXBuilder.h @@ -23,6 +23,8 @@ #include #include +// Basic hex/byte conversion class to be used by hash builders + class HEXBuilder { public: static size_t hex2bytes(unsigned char *out, size_t maxlen, String &in); diff --git a/cores/esp32/HashBuilder.cpp b/cores/esp32/HashBuilder.cpp new file mode 100644 index 00000000000..be3f67e2f78 --- /dev/null +++ b/cores/esp32/HashBuilder.cpp @@ -0,0 +1,38 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "HashBuilder.h" + +void HashBuilder::add(const char *data) { + add((const uint8_t *)data, strlen(data)); +} + +void HashBuilder::add(String data) { + add(data.c_str()); +} + +void HashBuilder::addHexString(const char *data) { + size_t len = strlen(data); + uint8_t *tmp = (uint8_t *)malloc(len / 2); + if (tmp == NULL) { + return; + } + hex2bytes(tmp, len / 2, data); + add(tmp, len / 2); + free(tmp); +} + +void HashBuilder::addHexString(String data) { + addHexString(data.c_str()); +} diff --git a/cores/esp32/HashBuilder.h b/cores/esp32/HashBuilder.h index 77d1c71dbde..dc624043427 100644 --- a/cores/esp32/HashBuilder.h +++ b/cores/esp32/HashBuilder.h @@ -20,29 +20,44 @@ #include "HEXBuilder.h" +/* Try to prevent most compilers from optimizing out clearing of memory that + * becomes unaccessible after this function is called. This is mostly the case + * for clearing local stack variables at the end of a function. This is not + * exactly perfect, i.e., someone could come up with a compiler that figures out + * the pointer is pointing to memset and then end up optimizing the call out, so + * try go a bit further by storing the first octet (now zero) to make this even + * a bit more difficult to optimize out. Once memset_s() is available, that + * could be used here instead. */ +static void *(*const volatile memset_func)(void *, int, size_t) = memset; +static uint8_t forced_memzero_val; + +static inline void forced_memzero(void *ptr, size_t len) { + memset_func(ptr, 0, len); + if (len) { + forced_memzero_val = ((uint8_t *)ptr)[0]; + } +} + +// Base class for hash builders + class HashBuilder : public HEXBuilder { public: virtual ~HashBuilder() {} virtual void begin() = 0; virtual void add(const uint8_t *data, size_t len) = 0; - virtual void add(const char *data) { - add((const uint8_t *)data, strlen(data)); - } - virtual void add(String data) { - add(data.c_str()); - } + void add(const char *data); + void add(String data); - virtual void addHexString(const char *data) = 0; - virtual void addHexString(String data) { - addHexString(data.c_str()); - } + void addHexString(const char *data); + void addHexString(String data); virtual bool addStream(Stream &stream, const size_t maxLen) = 0; virtual void calculate() = 0; virtual void getBytes(uint8_t *output) = 0; virtual void getChars(char *output) = 0; virtual String toString() = 0; + virtual size_t getHashSize() const = 0; }; #endif diff --git a/cores/esp32/MD5Builder.cpp b/cores/esp32/MD5Builder.cpp index cd8aa31b6cc..3c578491c13 100644 --- a/cores/esp32/MD5Builder.cpp +++ b/cores/esp32/MD5Builder.cpp @@ -17,9 +17,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include +#include "HEXBuilder.h" +#include "MD5Builder.h" void MD5Builder::begin(void) { memset(_buf, 0x00, ESP_ROM_MD5_DIGEST_LEN); @@ -30,17 +29,6 @@ void MD5Builder::add(const uint8_t *data, size_t len) { esp_rom_md5_update(&_ctx, data, len); } -void MD5Builder::addHexString(const char *data) { - size_t len = strlen(data); - uint8_t *tmp = (uint8_t *)malloc(len / 2); - if (tmp == NULL) { - return; - } - hex2bytes(tmp, len / 2, data); - add(tmp, len / 2); - free(tmp); -} - bool MD5Builder::addStream(Stream &stream, const size_t maxLen) { const int buf_size = 512; int maxLengthLeft = maxLen; diff --git a/cores/esp32/MD5Builder.h b/cores/esp32/MD5Builder.h index 5728bd3bac0..cbddf8f60e2 100644 --- a/cores/esp32/MD5Builder.h +++ b/cores/esp32/MD5Builder.h @@ -35,19 +35,18 @@ class MD5Builder : public HashBuilder { uint8_t _buf[ESP_ROM_MD5_DIGEST_LEN]; public: - void begin(void) override; - using HashBuilder::add; - void add(const uint8_t *data, size_t len) override; - - using HashBuilder::addHexString; - void addHexString(const char *data) override; + void begin(void) override; + void add(const uint8_t *data, size_t len) override; bool addStream(Stream &stream, const size_t maxLen) override; void calculate(void) override; void getBytes(uint8_t *output) override; void getChars(char *output) override; String toString(void) override; + size_t getHashSize() const override { + return ESP_ROM_MD5_DIGEST_LEN; + } }; #endif diff --git a/cores/esp32/WString.cpp b/cores/esp32/WString.cpp index 6632aa5f85d..7210dfbfbc4 100644 --- a/cores/esp32/WString.cpp +++ b/cores/esp32/WString.cpp @@ -910,7 +910,7 @@ long String::toInt(void) const { float String::toFloat(void) const { if (buffer()) { - return atof(buffer()); + return static_cast(atof(buffer())); } return 0; } diff --git a/cores/esp32/esp32-hal-hosted.c b/cores/esp32/esp32-hal-hosted.c new file mode 100644 index 00000000000..0bb8023432a --- /dev/null +++ b/cores/esp32/esp32-hal-hosted.c @@ -0,0 +1,185 @@ +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) + +#include "esp32-hal-hosted.h" +#include "esp32-hal-log.h" + +#include "esp_hosted_transport_config.h" +extern esp_err_t esp_hosted_init(); +extern esp_err_t esp_hosted_deinit(); + +static bool hosted_initialized = false; +static bool hosted_ble_active = false; +static bool hosted_wifi_active = false; + +static sdio_pin_config_t sdio_pin_config = { +#ifdef BOARD_HAS_SDIO_ESP_HOSTED + .pin_clk = BOARD_SDIO_ESP_HOSTED_CLK, + .pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD, + .pin_d0 = BOARD_SDIO_ESP_HOSTED_D0, + .pin_d1 = BOARD_SDIO_ESP_HOSTED_D1, + .pin_d2 = BOARD_SDIO_ESP_HOSTED_D2, + .pin_d3 = BOARD_SDIO_ESP_HOSTED_D3, + .pin_reset = BOARD_SDIO_ESP_HOSTED_RESET +#else + .pin_clk = CONFIG_ESP_SDIO_PIN_CLK, + .pin_cmd = CONFIG_ESP_SDIO_PIN_CMD, + .pin_d0 = CONFIG_ESP_SDIO_PIN_D0, + .pin_d1 = CONFIG_ESP_SDIO_PIN_D1, + .pin_d2 = CONFIG_ESP_SDIO_PIN_D2, + .pin_d3 = CONFIG_ESP_SDIO_PIN_D3, + .pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE +#endif +}; + +static bool hostedInit() { + if (!hosted_initialized) { + log_i("Initializing ESP-Hosted"); + log_d( + "SDIO pins: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", sdio_pin_config.pin_clk, sdio_pin_config.pin_cmd, sdio_pin_config.pin_d0, + sdio_pin_config.pin_d1, sdio_pin_config.pin_d2, sdio_pin_config.pin_d3, sdio_pin_config.pin_reset + ); + hosted_initialized = true; + struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); + conf.pin_clk.pin = sdio_pin_config.pin_clk; + conf.pin_cmd.pin = sdio_pin_config.pin_cmd; + conf.pin_d0.pin = sdio_pin_config.pin_d0; + conf.pin_d1.pin = sdio_pin_config.pin_d1; + conf.pin_d2.pin = sdio_pin_config.pin_d2; + conf.pin_d3.pin = sdio_pin_config.pin_d3; + conf.pin_reset.pin = sdio_pin_config.pin_reset; + // esp_hosted_sdio_set_config() will fail on second attempt but here temporarily to not cause exception on reinit + if (esp_hosted_sdio_set_config(&conf) != ESP_OK || esp_hosted_init() != ESP_OK) { + log_e("esp_hosted_init failed!"); + hosted_initialized = false; + return false; + } + log_i("ESP-Hosted initialized!"); + } + + // Attach pins to PeriMan here + // Slave chip model is CONFIG_IDF_SLAVE_TARGET + // sdio_pin_config.pin_clk + // sdio_pin_config.pin_cmd + // sdio_pin_config.pin_d0 + // sdio_pin_config.pin_d1 + // sdio_pin_config.pin_d2 + // sdio_pin_config.pin_d3 + // sdio_pin_config.pin_reset + + return true; +} + +static bool hostedDeinit() { + if (!hosted_initialized) { + log_e("ESP-Hosted is not initialized"); + return false; + } + + if (esp_hosted_deinit() != ESP_OK) { + log_e("esp_hosted_deinit failed!"); + return false; + } + + hosted_initialized = false; + return true; +} + +bool hostedInitBLE() { + log_i("Initializing ESP-Hosted for BLE"); + hosted_ble_active = true; + return hostedInit(); +} + +bool hostedInitWiFi() { + log_i("Initializing ESP-Hosted for WiFi"); + hosted_wifi_active = true; + return hostedInit(); +} + +bool hostedDeinitBLE() { + log_i("Deinitializing ESP-Hosted for BLE"); + hosted_ble_active = false; + if (!hosted_wifi_active) { + return hostedDeinit(); + } else { + log_i("ESP-Hosted is still being used by Wi-Fi. Skipping deinit."); + return true; + } +} + +bool hostedDeinitWiFi() { + log_i("Deinitializing ESP-Hosted for WiFi"); + hosted_wifi_active = false; + if (!hosted_ble_active) { + return hostedDeinit(); + } else { + log_i("ESP-Hosted is still being used by BLE. Skipping deinit."); + return true; + } +} + +bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { + if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) { + log_e("All SDIO pins must be defined"); + return false; + } + + if (hosted_initialized) { + int8_t current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, current_rst; + hostedGetPins(¤t_clk, ¤t_cmd, ¤t_d0, ¤t_d1, ¤t_d2, ¤t_d3, ¤t_rst); + log_e("SDIO pins must be set before ESP-Hosted is initialized"); + log_e( + "Current pins used: clk=%d, cmd=%d, d0=%d, d1=%d, d2=%d, d3=%d, rst=%d", current_clk, current_cmd, current_d0, current_d1, current_d2, current_d3, + current_rst + ); + return false; + } + + sdio_pin_config.pin_clk = clk; + sdio_pin_config.pin_cmd = cmd; + sdio_pin_config.pin_d0 = d0; + sdio_pin_config.pin_d1 = d1; + sdio_pin_config.pin_d2 = d2; + sdio_pin_config.pin_d3 = d3; + sdio_pin_config.pin_reset = rst; + return true; +} + +void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst) { + *clk = sdio_pin_config.pin_clk; + *cmd = sdio_pin_config.pin_cmd; + *d0 = sdio_pin_config.pin_d0; + *d1 = sdio_pin_config.pin_d1; + *d2 = sdio_pin_config.pin_d2; + *d3 = sdio_pin_config.pin_d3; + *rst = sdio_pin_config.pin_reset; +} + +bool hostedIsBLEActive() { + return hosted_ble_active; +} + +bool hostedIsWiFiActive() { + return hosted_wifi_active; +} + +bool hostedIsInitialized() { + return hosted_initialized; +} + +#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */ diff --git a/cores/esp32/esp32-hal-hosted.h b/cores/esp32/esp32-hal-hosted.h new file mode 100644 index 00000000000..0a916bfac85 --- /dev/null +++ b/cores/esp32/esp32-hal-hosted.h @@ -0,0 +1,53 @@ +// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MAIN_ESP32_HAL_HOSTED_H_ +#define MAIN_ESP32_HAL_HOSTED_H_ + +#include "sdkconfig.h" +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) + +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t pin_clk; + uint8_t pin_cmd; + uint8_t pin_d0; + uint8_t pin_d1; + uint8_t pin_d2; + uint8_t pin_d3; + uint8_t pin_reset; +} sdio_pin_config_t; + +bool hostedInitBLE(); +bool hostedInitWiFi(); +bool hostedDeinitBLE(); +bool hostedDeinitWiFi(); +bool hostedIsInitialized(); +bool hostedIsBLEActive(); +bool hostedIsWiFiActive(); +bool hostedSetPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst); +void hostedGetPins(int8_t *clk, int8_t *cmd, int8_t *d0, int8_t *d1, int8_t *d2, int8_t *d3, int8_t *rst); + +#ifdef __cplusplus +} +#endif + +#endif /* defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) || defined(CONFIG_ESP_WIFI_REMOTE_ENABLED) */ +#endif /* MAIN_ESP32_HAL_HOSTED_H_ */ diff --git a/cores/esp32/esp32-hal-i2c-slave.c b/cores/esp32/esp32-hal-i2c-slave.c index da3a819387a..1f23b7832f7 100644 --- a/cores/esp32/esp32-hal-i2c-slave.c +++ b/cores/esp32/esp32-hal-i2c-slave.c @@ -618,7 +618,7 @@ static bool i2c_slave_check_line_state(int8_t sda, int8_t scl) { log_w("Recovered after %d Cycles", a); gpio_set_level(sda, 0); // start i2c_slave_delay_us(5); - for (uint8_t a = 0; a < 9; a++) { + for (uint8_t b = 0; b < 9; b++) { gpio_set_level(scl, 1); i2c_slave_delay_us(5); gpio_set_level(scl, 0); diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index ffb24db4599..1847f6af839 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -784,17 +784,23 @@ void analogWrite(uint8_t pin, int value) { } void analogWriteFrequency(uint8_t pin, uint32_t freq) { - if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) { - log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first"); - return; + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { // if pin is attached to LEDC change frequency, otherwise update the global frequency + if (ledcChangeFrequency(pin, freq, analog_resolution) == 0) { + log_e("analogWrite frequency cant be set due to selected resolution! Try to adjust resolution first"); + return; + } } analog_frequency = freq; } void analogWriteResolution(uint8_t pin, uint8_t resolution) { - if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) { - log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first"); - return; + ledc_channel_handle_t *bus = (ledc_channel_handle_t *)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC); + if (bus != NULL) { // if pin is attached to LEDC change resolution, otherwise update the global resolution + if (ledcChangeFrequency(pin, analog_frequency, resolution) == 0) { + log_e("analogWrite resolution cant be set due to selected frequency! Try to adjust frequency first"); + return; + } } analog_resolution = resolution; } diff --git a/cores/esp32/esp32-hal-misc.c b/cores/esp32/esp32-hal-misc.c index 4c8a4e5beab..41e2cf71543 100644 --- a/cores/esp32/esp32-hal-misc.c +++ b/cores/esp32/esp32-hal-misc.c @@ -308,7 +308,7 @@ void initArduino() { if (err) { log_e("Failed to initialize NVS! Error: %u", err); } -#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED +#if (defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED)) && SOC_BT_SUPPORTED && __has_include("esp_bt.h") if (!btInUse()) { esp_bt_controller_mem_release(ESP_BT_MODE_BTDM); } diff --git a/cores/esp32/esp32-hal-periman.c b/cores/esp32/esp32-hal-periman.c index 6ef3c3d984a..2ba8ffde5f7 100644 --- a/cores/esp32/esp32-hal-periman.c +++ b/cores/esp32/esp32-hal-periman.c @@ -16,7 +16,7 @@ typedef struct ATTR_PACKED { int8_t bus_channel; } peripheral_pin_item_t; -static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX]; +static peripheral_bus_deinit_cb_t deinit_functions[ESP32_BUS_TYPE_MAX] = {NULL}; static peripheral_pin_item_t pins[SOC_GPIO_PIN_COUNT]; #define GPIO_NOT_VALID(p) ((p >= SOC_GPIO_PIN_COUNT) || ((SOC_GPIO_VALID_GPIO_MASK & (1ULL << p)) == 0)) @@ -236,6 +236,14 @@ bool perimanSetBusDeinit(peripheral_bus_type_t type, peripheral_bus_deinit_cb_t 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; + } + return deinit_functions[type]; +} + bool perimanPinIsValid(uint8_t pin) { return !(GPIO_NOT_VALID(pin)); } diff --git a/cores/esp32/esp32-hal-periman.h b/cores/esp32/esp32-hal-periman.h index 217d62b8741..08b8c791ea7 100644 --- a/cores/esp32/esp32-hal-periman.h +++ b/cores/esp32/esp32-hal-periman.h @@ -134,6 +134,10 @@ 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); +// Get the peripheral destructor callback. It allows changing/restoring the peripheral pin function detaching, if necessary +// returns NULL if none is set +peripheral_bus_deinit_cb_t perimanGetBusDeinit(peripheral_bus_type_t type); + // Check if given pin is a valid GPIO number bool perimanPinIsValid(uint8_t pin); diff --git a/cores/esp32/esp32-hal-rmt.c b/cores/esp32/esp32-hal-rmt.c index 7bca1a1b529..6d8a6c7dfae 100644 --- a/cores/esp32/esp32-hal-rmt.c +++ b/cores/esp32/esp32-hal-rmt.c @@ -285,7 +285,7 @@ bool rmtDeinit(int pin) { return false; } -static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, bool loop, uint32_t timeout_ms) { +static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool blocking, uint32_t loop, uint32_t timeout_ms) { rmt_bus_handle_t bus = _rmtGetBus(pin, __FUNCTION__); if (bus == NULL) { return false; @@ -303,11 +303,21 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl } } +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE log_v("GPIO: %d - Request: %d RMT Symbols - %s - Timeout: %d", pin, num_rmt_symbols, blocking ? "Blocking" : "Non-Blocking", timeout_ms); - log_v( - "GPIO: %d - Currently in Loop Mode: [%s] | Asked to Loop: %s, LoopCancel: %s", pin, bus->rmt_ch_is_looping ? "YES" : "NO", loop ? "YES" : "NO", - loopCancel ? "YES" : "NO" - ); + // loop parameter semantics: + // loop == 0: no looping (single transmission) + // loop == 1: infinite looping + // loop > 1: transmit the data 'loop' times + { + char buf[17]; // placeholder for up to maximum uint32_t value (4294967295) = 10 digits + " times" (6 chars) + null terminator (17 bytes) + snprintf(buf, sizeof(buf), "%lu times", loop); + log_v( + "GPIO: %d - Currently in Loop Mode: [%s] | Loop Request: [%s], LoopCancel: [%s]", pin, bus->rmt_ch_is_looping ? "YES" : "NO", + loop == 0 ? "NO" : (loop == 1 ? "FOREVER" : buf), loopCancel ? "YES" : "NO" + ); + } +#endif if ((xEventGroupGetBits(bus->rmt_events) & RMT_FLAG_TX_DONE) == 0) { log_v("GPIO %d - RMT Write still pending to be completed.", pin); @@ -336,8 +346,8 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl bus->rmt_ch_is_looping = false; } else { // new writing | looping request // looping | Writing over a previous looping state is valid - if (loop) { - transmit_cfg.loop_count = -1; // enable infinite loop mode + if (loop > 0) { + transmit_cfg.loop_count = (loop == 1) ? -1 : loop; // keeps RMT_FLAG_TX_DONE set - it never changes } else { // looping mode never sets this flag (IDF 5.1) in the callback @@ -348,8 +358,10 @@ static bool _rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, bool bl retCode = false; log_w("GPIO %d - RMT Transmission failed.", pin); } else { // transmit OK - if (loop) { - bus->rmt_ch_is_looping = true; // for ever... until a channel canceling or new writing + if (loop > 0) { + // rmt_ch_is_looping is used as a flag to indicate that RMT is in looping execution in order to + // be canceled whenever a new _rmtWrite() is executed while it is looping + bus->rmt_ch_is_looping = true; } else { if (blocking) { // wait for transmission confirmation | timeout @@ -402,15 +414,36 @@ static bool _rmtRead(int pin, rmt_data_t *data, size_t *num_rmt_symbols, bool wa } bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeout_ms) { - return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, false /*looping*/, timeout_ms); + return _rmtWrite(pin, data, num_rmt_symbols, true /*blocks*/, 0 /*looping*/, timeout_ms); } bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols) { - return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, false /*looping*/, 0 /*N/A*/); + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/); } bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols) { - return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, true /*looping*/, 0 /*N/A*/); + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 1 /*looping*/, 0 /*N/A*/); +} + +// Same as rmtWriteLooping(...) but it transmits the data a fixed number of times ("loop_count"). +// loop_count == 0 is invalid (no transmission); loop_count == 1 transmits once (no looping); loop_count > 1 transmits the data repeatedly (looping). +bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count) { + if (loop_count == 0) { + log_e("RMT TX GPIO %d : Invalid loop_count (%u). Must be at least 1.", pin, loop_count); + return false; + } + if (loop_count == 1) { + // send the RMT symbols once using non-blocking write (single non-looping transmission) + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, 0 /*looping*/, 0 /*N/A*/); + } else { + // write the RMT symbols for loop_count times +#if SOC_RMT_SUPPORT_TX_LOOP_COUNT + return _rmtWrite(pin, data, num_rmt_symbols, false /*blocks*/, loop_count /*looping*/, 0 /*N/A*/); +#else + log_e("RMT TX GPIO %d : Loop Count is not supported. Writing failed.", pin); + return false; +#endif + } } bool rmtTransmitCompleted(int pin) { diff --git a/cores/esp32/esp32-hal-rmt.h b/cores/esp32/esp32-hal-rmt.h index c15eadfbcd1..2698f9dd898 100644 --- a/cores/esp32/esp32-hal-rmt.h +++ b/cores/esp32/esp32-hal-rmt.h @@ -124,7 +124,9 @@ bool rmtWrite(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t timeou bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); /** - Writing data up to the reserved memsize, looping continuously + Writing data up to the reserved memsize, looping continuously (rmtWriteLooping()) or fixed + number of times (rmtWriteRepeated()) + is a 32 bits structure as defined by rmt_data_t type. It is possible to use the macro RMT_SYMBOLS_OF(data), if data is an array of rmt_data_t @@ -133,9 +135,11 @@ bool rmtWriteAsync(int pin, rmt_data_t *data, size_t num_rmt_symbols); Non-Blocking mode - returns right after execution Returns on execution success, otherwise - will return always while it is looping. + will return always while it is looping mode. + looping mode is active for rmtWriteLooping() and for rmtWriteRepeated() when loop_count > 1. */ bool rmtWriteLooping(int pin, rmt_data_t *data, size_t num_rmt_symbols); +bool rmtWriteRepeated(int pin, rmt_data_t *data, size_t num_rmt_symbols, uint32_t loop_count); /** Checks if transmission is completed and the rmtChannel ready for transmitting new data. diff --git a/cores/esp32/esp32-hal-time.c b/cores/esp32/esp32-hal-time.c index 074e999be71..e66a68fa271 100644 --- a/cores/esp32/esp32-hal-time.c +++ b/cores/esp32/esp32-hal-time.c @@ -22,24 +22,24 @@ #endif static void setTimeZone(long offset, int daylight) { - char cst[17] = {0}; - char cdt[17] = "DST"; - char tz[33] = {0}; + char cst[21] = {0}; + char cdt[21] = "DST"; + char tz[41] = {0}; if (offset % 3600) { - sprintf(cst, "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); + snprintf(cst, sizeof(cst), "UTC%ld:%02u:%02u", offset / 3600, abs((offset % 3600) / 60), abs(offset % 60)); } else { - sprintf(cst, "UTC%ld", offset / 3600); + snprintf(cst, sizeof(cst), "UTC%ld", offset / 3600); } if (daylight != 3600) { long tz_dst = offset - daylight; if (tz_dst % 3600) { - sprintf(cdt, "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); + snprintf(cdt, sizeof(cdt), "DST%ld:%02u:%02u", tz_dst / 3600, abs((tz_dst % 3600) / 60), abs(tz_dst % 60)); } else { - sprintf(cdt, "DST%ld", tz_dst / 3600); + snprintf(cdt, sizeof(cdt), "DST%ld", tz_dst / 3600); } } - sprintf(tz, "%s%s", cst, cdt); + snprintf(tz, sizeof(tz), "%s%s", cst, cdt); setenv("TZ", tz, 1); tzset(); } diff --git a/cores/esp32/esp32-hal-timer.c b/cores/esp32/esp32-hal-timer.c index ec6c507358e..85e007143bd 100644 --- a/cores/esp32/esp32-hal-timer.c +++ b/cores/esp32/esp32-hal-timer.c @@ -22,6 +22,12 @@ #include "esp_clk_tree.h" #endif +#if CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM +#define TIMER_IRAM IRAM_ATTR +#else +#define TIMER_IRAM +#endif + typedef void (*voidFuncPtr)(void); typedef void (*voidFuncPtrArg)(void *); @@ -36,9 +42,11 @@ struct timer_struct_t { bool timer_started; }; -inline uint64_t timerRead(hw_timer_t *timer) { +inline TIMER_IRAM uint64_t timerRead(hw_timer_t *timer) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return 0; } uint64_t value; @@ -46,17 +54,21 @@ inline uint64_t timerRead(hw_timer_t *timer) { return value; } -void timerWrite(hw_timer_t *timer, uint64_t val) { +void TIMER_IRAM timerWrite(hw_timer_t *timer, uint64_t val) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return; } gptimer_set_raw_count(timer->timer_handle, val); } -void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count) { +void TIMER_IRAM timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return; } esp_err_t err = ESP_OK; @@ -67,7 +79,9 @@ void timerAlarm(hw_timer_t *timer, uint64_t alarm_value, bool autoreload, uint64 }; err = gptimer_set_alarm_action(timer->timer_handle, &alarm_cfg); if (err != ESP_OK) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer Alarm Write failed, error num=%d", err); +#endif } } @@ -80,27 +94,33 @@ uint32_t timerGetFrequency(hw_timer_t *timer) { return frequency; } -void timerStart(hw_timer_t *timer) { +void TIMER_IRAM timerStart(hw_timer_t *timer) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return; } gptimer_start(timer->timer_handle); timer->timer_started = true; } -void timerStop(hw_timer_t *timer) { +void TIMER_IRAM timerStop(hw_timer_t *timer) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return; } gptimer_stop(timer->timer_handle); timer->timer_started = false; } -void timerRestart(hw_timer_t *timer) { +void TIMER_IRAM timerRestart(hw_timer_t *timer) { if (timer == NULL) { +#ifndef CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM log_e("Timer handle is NULL"); +#endif return; } gptimer_set_raw_count(timer->timer_handle, 0); diff --git a/cores/esp32/esp32-hal-touch-ng.c b/cores/esp32/esp32-hal-touch-ng.c index 888a299ec0c..98f9052d18c 100644 --- a/cores/esp32/esp32-hal-touch-ng.c +++ b/cores/esp32/esp32-hal-touch-ng.c @@ -12,11 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "soc/soc_caps.h" +#include "esp_idf_version.h" #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 for now +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 -#include "driver/touch_sens.h" #include "esp32-hal-touch-ng.h" #include "esp32-hal-periman.h" @@ -37,11 +37,24 @@ typedef struct { static TouchInterruptHandle_t __touchInterruptHandlers[SOC_TOUCH_SENSOR_NUM] = { 0, }; - -static uint8_t _sample_num = 1; +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +static uint8_t _sample_num = 1; // only one sample configuration supported +static float _duration_ms = 5.0f; +static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5; +static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_1V7; +static touch_intr_trig_mode_t _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH; +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +static uint8_t _sample_num = 1; // only one sample configuration supported +static uint32_t _chg_times = 500; +static touch_volt_lim_l_t _volt_low = TOUCH_VOLT_LIM_L_0V5; +static touch_volt_lim_h_t _volt_high = TOUCH_VOLT_LIM_H_2V2; +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 +static uint8_t _sample_num = 1; // TODO: can be extended to multiple samples static uint32_t _div_num = 1; static uint8_t _coarse_freq_tune = 1; static uint8_t _fine_freq_tune = 1; +#endif + static uint8_t used_pads = 0; static uint32_t __touchSleepTime = 256; @@ -156,15 +169,28 @@ bool touchBenchmarkThreshold(uint8_t pad) { // Reconfigure passed pad with new threshold uint32_t benchmark[_sample_num] = {}; +#if SOC_TOUCH_SUPPORT_BENCHMARK // ESP32S2, ESP32S3,ESP32P4 if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_BENCHMARK, benchmark) != ESP_OK) { log_e("Touch channel read data failed!"); return false; } +#else + if (touch_channel_read_data(touch_channel_handle[pad], TOUCH_CHAN_DATA_TYPE_SMOOTH, benchmark) != ESP_OK) { + log_e("Touch channel read data failed!"); + return false; + } +#endif + /* Calculate the proper active thresholds regarding the initial benchmark */ - touch_channel_config_t chan_cfg = {}; + touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG(); for (int i = 0; i < _sample_num; i++) { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + chan_cfg.abs_active_thresh[i] = (uint32_t)(benchmark[i] * (1 - s_thresh2bm_ratio)); + log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.abs_active_thresh[i]); +#else chan_cfg.active_thresh[i] = (uint32_t)(benchmark[i] * s_thresh2bm_ratio); log_v("Configured [CH %d] sample %d: benchmark = %" PRIu32 ", threshold = %" PRIu32 "\t", pad, i, benchmark[i], chan_cfg.active_thresh[i]); +#endif } /* Update the channel configuration */ if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) { @@ -178,17 +204,26 @@ static bool touchDetachBus(void *pin) { int8_t pad = digitalPinToTouchChannel((int)(pin - 1)); channels_initialized[pad] = false; //disable touch pad and delete the channel + if (!touchStop()) { + log_e("touchStop() failed!"); + return false; + } + if (!touchDisable()) { + log_e("touchDisable() failed!"); + return false; + } touch_sensor_del_channel(touch_channel_handle[pad]); used_pads--; if (used_pads == 0) { - touchStop(); - touchDisable(); if (touch_sensor_del_controller(touch_sensor_handle) != ESP_OK) //deinit touch module, as no pads are used { log_e("Touch module deinit failed!"); return false; } initialized = false; + } else { + touchEnable(); + touchStart(); } return true; } @@ -198,21 +233,40 @@ static void __touchInit() { return; } // Support only one sample configuration for now +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V1_DEFAULT_SAMPLE_CONFIG(_duration_ms, _volt_low, _volt_high); +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 + touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V2_DEFAULT_SAMPLE_CONFIG(_chg_times, _volt_low, _volt_high); +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 touch_sensor_sample_config_t single_sample_cfg = TOUCH_SENSOR_V3_DEFAULT_SAMPLE_CONFIG(_div_num, _coarse_freq_tune, _fine_freq_tune); +#endif touch_sensor_sample_config_t sample_cfg[_sample_num] = {}; sample_cfg[0] = single_sample_cfg; - /* Allocate new touch controller handle */ touch_sensor_config_t sens_cfg = { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + .power_on_wait_us = __touchSleepTime, + .meas_interval_us = __touchMeasureTime, + .intr_trig_mode = _intr_trig_mode, + .intr_trig_group = TOUCH_INTR_TRIG_GROUP_BOTH, + .sample_cfg_num = _sample_num, + .sample_cfg = sample_cfg, +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 + .power_on_wait_us = __touchSleepTime, + .meas_interval_us = __touchMeasureTime, + .max_meas_time_us = 0, + .sample_cfg_num = _sample_num, + .sample_cfg = sample_cfg, +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 .power_on_wait_us = __touchSleepTime, .meas_interval_us = __touchMeasureTime, .max_meas_time_us = 0, .output_mode = TOUCH_PAD_OUT_AS_CLOCK, .sample_cfg_num = _sample_num, .sample_cfg = sample_cfg, +#endif }; - // touch_sensor_config_t sens_cfg = TOUCH_SENSOR_DEFAULT_BASIC_CONFIG(_sample_num, sample_cfg); if (touch_sensor_new_controller(&sens_cfg, &touch_sensor_handle) != ESP_OK) { goto err; } @@ -225,14 +279,10 @@ static void __touchInit() { } /* Register the touch sensor on_active and on_inactive callbacks */ - touch_event_callbacks_t callbacks = { - .on_active = __touchOnActiveISR, - .on_inactive = __touchOnInactiveISR, - .on_measure_done = NULL, - .on_scan_done = NULL, - .on_timeout = NULL, - .on_proximity_meas_done = NULL, - }; + touch_event_callbacks_t callbacks = {0}; + callbacks.on_active = __touchOnActiveISR; + callbacks.on_inactive = __touchOnInactiveISR; + if (touch_sensor_register_callbacks(touch_sensor_handle, &callbacks, NULL) != ESP_OK) { goto err; } @@ -253,9 +303,7 @@ static void __touchChannelInit(int pad) { // Initial setup with default Threshold __touchInterruptHandlers[pad].fn = NULL; - touch_channel_config_t chan_cfg = { - .active_thresh = {1000} // default threshold, will be updated after benchmark - }; + touch_channel_config_t chan_cfg = TOUCH_CHANNEL_DEFAULT_CONFIG(); if (!touchStop() || !touchDisable()) { log_e("Touch sensor stop and disable failed!"); @@ -323,8 +371,21 @@ static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Ar __touchInterruptHandlers[pad].arg = NULL; } else { // attach ISR User Call - __touchInit(); - __touchChannelInit(pad); + if (perimanGetPinBus(pin, ESP32_BUS_TYPE_TOUCH) == NULL) { + perimanSetBusDeinit(ESP32_BUS_TYPE_TOUCH, touchDetachBus); + if (!perimanClearPinBus(pin)) { + log_e("Failed to clear pin bus"); + return; + } + __touchInit(); + __touchChannelInit(pad); + + if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_TOUCH, (void *)(pin + 1), -1, pad)) { + touchDetachBus((void *)(pin + 1)); + log_e("Failed to set bus to Peripheral manager"); + return; + } + } __touchInterruptHandlers[pad].fn = userFunc; __touchInterruptHandlers[pad].callWithArgs = callWithArgs; __touchInterruptHandlers[pad].arg = Args; @@ -338,7 +399,11 @@ static void __touchConfigInterrupt(uint8_t pin, void (*userFunc)(void), void *Ar touch_channel_config_t chan_cfg = {}; for (int i = 0; i < _sample_num; i++) { +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + chan_cfg.abs_active_thresh[i] = threshold; +#else chan_cfg.active_thresh[i] = threshold; +#endif } if (touch_sensor_reconfig_channel(touch_channel_handle[pad], &chan_cfg) != ESP_OK) { @@ -375,7 +440,6 @@ bool touchInterruptGetLastStatus(uint8_t pin) { if (pad < 0) { return false; } - return __touchInterruptHandlers[pad].lastStatusIsPressed; } @@ -405,9 +469,13 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold) { touch_sleep_config_t deep_slp_cfg = { .slp_wakeup_lvl = TOUCH_DEEP_SLEEP_WAKEUP, +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 + .deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration +#else // SOC_TOUCH_SENSOR_VERSION 2 and 3// ESP32S2, ESP32S3, ESP32P4 .deep_slp_chan = touch_channel_handle[pad], .deep_slp_thresh = {threshold}, .deep_slp_sens_cfg = NULL, // Use the original touch sensor configuration +#endif }; // Register the deep sleep wake-up @@ -434,6 +502,29 @@ void touchSetTiming(float measure, uint32_t sleep) { __touchMeasureTime = measure; } +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) { + if (initialized) { + log_e("Touch sensor already initialized. Cannot set configuration."); + return; + } + _duration_ms = duration_ms; + _volt_low = volt_low; + _volt_high = volt_high; +} + +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high) { + if (initialized) { + log_e("Touch sensor already initialized. Cannot set configuration."); + return; + } + _chg_times = chg_times; + _volt_low = volt_low; + _volt_high = volt_high; +} + +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune) { if (initialized) { log_e("Touch sensor already initialized. Cannot set configuration."); @@ -443,11 +534,22 @@ void touchSetConfig(uint32_t div_num, uint8_t coarse_freq_tune, uint8_t fine_fre _coarse_freq_tune = coarse_freq_tune; _fine_freq_tune = fine_freq_tune; } +#endif + +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +void touchInterruptSetThresholdDirection(bool mustbeLower) { + if (mustbeLower) { + _intr_trig_mode = TOUCH_INTR_TRIG_ON_BELOW_THRESH; + } else { + _intr_trig_mode = TOUCH_INTR_TRIG_ON_ABOVE_THRESH; + } +} +#endif extern touch_value_t touchRead(uint8_t) __attribute__((weak, alias("__touchRead"))); extern void touchAttachInterrupt(uint8_t, voidFuncPtr, touch_value_t) __attribute__((weak, alias("__touchAttachInterrupt"))); extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value_t) __attribute__((weak, alias("__touchAttachArgsInterrupt"))); extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt"))); -#endif /* SOC_TOUCH_SENSOR_VERSION == 3 */ +#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ diff --git a/cores/esp32/esp32-hal-touch-ng.h b/cores/esp32/esp32-hal-touch-ng.h index 0d4eb79ac58..ebe49466bb9 100644 --- a/cores/esp32/esp32-hal-touch-ng.h +++ b/cores/esp32/esp32-hal-touch-ng.h @@ -21,14 +21,31 @@ #define MAIN_ESP32_HAL_TOUCH_NEW_H_ #include "soc/soc_caps.h" +#include "esp_idf_version.h" + #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 #ifdef __cplusplus extern "C" { #endif #include "esp32-hal.h" +#include "driver/touch_sens.h" + +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { \ + .abs_active_thresh = {1000}, .charge_speed = TOUCH_CHARGE_SPEED_7, .init_charge_volt = TOUCH_INIT_CHARGE_VOLT_DEFAULT, \ + .group = TOUCH_CHAN_TRIG_GROUP_BOTH, \ + } +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32-S2 & ESP32-S3 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { .active_thresh = {2000}, .charge_speed = TOUCH_CHARGE_SPEED_7, .init_charge_volt = TOUCH_INIT_CHARGE_VOLT_DEFAULT, } +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32-P4 +#define TOUCH_CHANNEL_DEFAULT_CONFIG() \ + { .active_thresh = {1000}, } +#endif typedef uint32_t touch_value_t; @@ -40,11 +57,39 @@ typedef uint32_t touch_value_t; **/ void touchSetTiming(float measure, uint32_t sleep); +#if SOC_TOUCH_SENSOR_VERSION == 1 // ESP32 +/* + * @param[in] duration_ms The measurement duration of the touch channel + * @param[in] volt_low The low voltage limit of the touch channel + * @param[in] volt_high The high voltage limit of the touch channel + */ +void touchSetConfig(float duration_ms, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high); + +#elif SOC_TOUCH_SENSOR_VERSION == 2 // ESP32S2, ESP32S3 +/* + * @param[in] chg_times The charge times of the touch channel + * @param[in] volt_low The low voltage limit of the touch channel + * @param[in] volt_high The high voltage limit of the touch channel + */ +void touchSetConfig(uint32_t chg_times, touch_volt_lim_l_t volt_low, touch_volt_lim_h_t volt_high); + +#elif SOC_TOUCH_SENSOR_VERSION == 3 // ESP32P4 /* * Tune the touch pad frequency. * Note: Must be called before setting up touch pads */ void touchSetConfig(uint32_t _div_num, uint8_t coarse_freq_tune, uint8_t fine_freq_tune); +#endif + +#if SOC_TOUCH_SENSOR_VERSION == 1 +/* + * Specific functions to ESP32 + * Tells the driver if it shall activate the ISR if the sensor is Lower or Higher than the Threshold + * Default if Lower. + * Note: Must be called before setting up touch pads + **/ +void touchInterruptSetThresholdDirection(bool mustbeLower); +#endif /* * Read touch pad value. @@ -86,6 +131,6 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold); } #endif -#endif /* SOC_TOUCH_SENSOR_VERSION == 3 */ +#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0) || SOC_TOUCH_SENSOR_VERSION == 3 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ #endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/cores/esp32/esp32-hal-touch.c b/cores/esp32/esp32-hal-touch.c index 701bf6d16c9..10482234de0 100644 --- a/cores/esp32/esp32-hal-touch.c +++ b/cores/esp32/esp32-hal-touch.c @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "soc/soc_caps.h" +#include "esp_idf_version.h" #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 #include "driver/touch_sensor.h" #include "esp32-hal-touch.h" @@ -325,5 +326,5 @@ extern void touchAttachInterruptArg(uint8_t, voidArgFuncPtr, void *, touch_value extern void touchDetachInterrupt(uint8_t) __attribute__((weak, alias("__touchDettachInterrupt"))); extern void touchSetCycles(uint16_t, uint16_t) __attribute__((weak, alias("__touchSetCycles"))); -#endif /* SOC_TOUCH_SENSOR_VERSION <= 2 */ +#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ diff --git a/cores/esp32/esp32-hal-touch.h b/cores/esp32/esp32-hal-touch.h index 4b06c7db766..44c99dce206 100644 --- a/cores/esp32/esp32-hal-touch.h +++ b/cores/esp32/esp32-hal-touch.h @@ -21,8 +21,10 @@ #define MAIN_ESP32_HAL_TOUCH_H_ #include "soc/soc_caps.h" +#include "esp_idf_version.h" + #if SOC_TOUCH_SENSOR_SUPPORTED -#if SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32 ESP32S2 ESP32S3 +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 // ESP32, ESP32S2, ESP32S3 #ifdef __cplusplus extern "C" { @@ -98,6 +100,6 @@ void touchSleepWakeUpEnable(uint8_t pin, touch_value_t threshold); } #endif -#endif /* SOC_TOUCH_SENSOR_VERSION <= 2 */ +#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 5, 0) && SOC_TOUCH_SENSOR_VERSION <= 2 */ #endif /* SOC_TOUCH_SENSOR_SUPPORTED */ #endif /* MAIN_ESP32_HAL_TOUCH_H_ */ diff --git a/cores/esp32/esp32-hal-uart.c b/cores/esp32/esp32-hal-uart.c index 8bd446a8eb8..7b194679caa 100644 --- a/cores/esp32/esp32-hal-uart.c +++ b/cores/esp32/esp32-hal-uart.c @@ -59,7 +59,7 @@ struct uart_struct_t { uint16_t _rx_buffer_size, _tx_buffer_size; // UART RX and TX buffer sizes bool _inverted; // UART inverted signal uint8_t _rxfifo_full_thrhd; // UART RX FIFO full threshold - int8_t _uart_clock_source; // UART Clock Source used when it is started using uartBegin() + int8_t _uart_clock_source; // UART Clock Source that should be used if user defines an specific one with setClockSource() }; #if CONFIG_DISABLE_HAL_LOCKS @@ -820,7 +820,7 @@ uart_t *uartBegin( uart_config.baud_rate = baudrate; #if SOC_UART_LP_NUM >= 1 if (uart_nr >= SOC_UART_HP_NUM) { // it is a LP UART NUM - if (uart->_uart_clock_source > 0) { + if (uart->_uart_clock_source >= 0) { uart_config.lp_source_clk = (soc_periph_lp_uart_clk_src_t)uart->_uart_clock_source; // use user defined LP UART clock log_v("Setting UART%d to user defined LP clock source (%d) ", uart_nr, uart->_uart_clock_source); } else { @@ -881,14 +881,7 @@ uart_t *uartBegin( uart->_tx_buffer_size = tx_buffer_size; uart->has_peek = false; uart->peek_byte = 0; -#if SOC_UART_LP_NUM >= 1 - if (uart_nr >= SOC_UART_HP_NUM) { - uart->_uart_clock_source = uart_config.lp_source_clk; - } else -#endif - { - uart->_uart_clock_source = uart_config.source_clk; - } + // uart->_uart_clock_source can only change by explicit user API request/call } UART_MUTEX_UNLOCK(); @@ -1149,10 +1142,9 @@ bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate) { } bool retCode = true; soc_module_clk_t newClkSrc = UART_SCLK_DEFAULT; - int8_t previousClkSrc = uart->_uart_clock_source; #if SOC_UART_LP_NUM >= 1 if (uart->num >= SOC_UART_HP_NUM) { // it is a LP UART NUM - if (uart->_uart_clock_source > 0) { + if (uart->_uart_clock_source >= 0) { newClkSrc = (soc_periph_lp_uart_clk_src_t)uart->_uart_clock_source; // use user defined LP UART clock log_v("Setting UART%d to user defined LP clock source (%d) ", uart->num, newClkSrc); } else { @@ -1187,13 +1179,10 @@ bool uartSetBaudRate(uart_t *uart, uint32_t baud_rate) { } } UART_MUTEX_LOCK(); - // if necessary, set the correct UART Clock Source before changing the baudrate - if (previousClkSrc < 0 || previousClkSrc != newClkSrc) { - HP_UART_SRC_CLK_ATOMIC() { - uart_ll_set_sclk(UART_LL_GET_HW(uart->num), newClkSrc); - } - uart->_uart_clock_source = newClkSrc; + HP_UART_SRC_CLK_ATOMIC() { + uart_ll_set_sclk(UART_LL_GET_HW(uart->num), newClkSrc); } + // uart->_uart_clock_source can only change by explicit user API request/call if (uart_set_baudrate(uart->num, baud_rate) == ESP_OK) { log_v("Setting UART%d baud rate to %ld.", uart->num, baud_rate); uart->_baudrate = baud_rate; @@ -1312,7 +1301,7 @@ bool uartSetClockSource(uint8_t uartNum, uart_sclk_t clkSrc) { { uart->_uart_clock_source = clkSrc; } - //log_i("UART%d set clock source to %d", uart->num, uart->_uart_clock_source); + log_v("UART%d set clock source to %d", uart->num, uart->_uart_clock_source); return true; } diff --git a/cores/esp32/esp32-hal.h b/cores/esp32/esp32-hal.h index 5ed99aeb205..84c73577d3e 100644 --- a/cores/esp32/esp32-hal.h +++ b/cores/esp32/esp32-hal.h @@ -100,6 +100,7 @@ void yield(void); #include "esp32-hal-psram.h" #include "esp32-hal-rgb-led.h" #include "esp32-hal-cpu.h" +#include "esp32-hal-hosted.h" void analogWrite(uint8_t pin, int value); void analogWriteFrequency(uint8_t pin, uint32_t freq); diff --git a/cores/esp32/main.cpp b/cores/esp32/main.cpp index 6c4d50a9a84..fb11ff4a5c7 100644 --- a/cores/esp32/main.cpp +++ b/cores/esp32/main.cpp @@ -44,10 +44,18 @@ __attribute__((weak)) bool shouldPrintChipDebugReport(void) { return false; } +// this function can be changed by the sketch using the macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) +__attribute__((weak)) uint64_t getArduinoSetupWaitTime_ms(void) { + return 0; +} + void loopTask(void *pvParameters) { #if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL) // sets UART0 (default console) RX/TX pins as already configured in boot or as defined in variants/pins_arduino.h Serial0.setPins(gpioNumberToDigitalPin(SOC_RX0), gpioNumberToDigitalPin(SOC_TX0)); + // time in ms that the sketch may wait before starting its execution - default is zero + // usually done for opening the Serial Monitor and seeing all debug messages + delay(getArduinoSetupWaitTime_ms()); #endif #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_DEBUG printBeforeSetupInfo(); diff --git a/cores/esp32/stdlib_noniso.c b/cores/esp32/stdlib_noniso.c index 0acb26c67b9..352eed4fc41 100644 --- a/cores/esp32/stdlib_noniso.c +++ b/cores/esp32/stdlib_noniso.c @@ -49,7 +49,7 @@ char *ltoa(long value, char *result, int base) { } char *out = result; - long quotient = abs(value); + long quotient = labs(value); do { const long tmp = quotient / base; diff --git a/docs/en/api/adc.rst b/docs/en/api/adc.rst index 434384f8d9b..f2245cc1e5d 100644 --- a/docs/en/api/adc.rst +++ b/docs/en/api/adc.rst @@ -52,10 +52,12 @@ This function will return analog value in millivolts (calibrated). analogReadResolution ^^^^^^^^^^^^^^^^^^^^ -This function is used to set the resolution of ``analogRead`` return value. Default is 12 bits (range from 0 to 4095) -for all chips except ESP32-S3 where default is 13 bits (range from 0 to 8191). +This function is used to set the resolution of ``analogRead`` return value. Default is 12 bits (range from 0 to 4095) for all chips. When different resolution is set, the values read will be shifted to match the given resolution. +.. note:: + For **ESP32-S2 chip revision v0.0**, the default ADC resolution is 13 bits (0-8191) due to the `ADC-112 errata `_. This is fixed in later revisions (v1.0+), which use the standard 12-bit resolution. + Range is 1 - 16 .The default value will be used, if this function is not used. .. note:: For the ESP32, the resolution is between 9 to12 and it will change the ADC hardware resolution. Else value will be shifted. diff --git a/docs/en/api/ledc.rst b/docs/en/api/ledc.rst index 6ea3437bbf5..d3d6da1e0f6 100644 --- a/docs/en/api/ledc.rst +++ b/docs/en/api/ledc.rst @@ -296,6 +296,7 @@ It is compatible with Arduinos analogWrite function. * ``pin`` select the GPIO pin. * ``value`` select the duty cycle of pwm. + * range is from 0 (always off) to 255 (always on). analogWriteResolution diff --git a/docs/en/api/spi.rst b/docs/en/api/spi.rst index c82b0de5ccb..27c78be9569 100644 --- a/docs/en/api/spi.rst +++ b/docs/en/api/spi.rst @@ -10,7 +10,7 @@ For some APIs, the reference to be used is the same as the Arduino Core. Arduino API Reference --------------------- -`SPI Reference `_ +`SPI Reference `_ `SPI Description `_ diff --git a/docs/en/contributing.rst b/docs/en/contributing.rst index 7a7b99894eb..ae07bb57706 100644 --- a/docs/en/contributing.rst +++ b/docs/en/contributing.rst @@ -425,6 +425,10 @@ The ``ci.json`` file is used to specify how the test suite and sketches will han * ``fqbn``: A dictionary that specifies the FQBNs that will be used to compile the sketch. The key is the target name and the value is a list of FQBNs. The `default FQBNs `_ are used if this field is not specified. This overrides the default FQBNs and the ``fqbn_append`` field. +* ``libs``: A list of libraries that are required to run the test suite. The libraries will be installed automatically if they are not already present. + Libraries are installed using the ``arduino-cli lib install`` command, so you can specify libraries by name + version (e.g., ``AudioZero@1.0.0``) + or by URL (e.g., ``https://github.com/arduino-libraries/WiFi101.git``). + More information can be found in the `Arduino CLI documentation `_. The ``wifi`` test suite is a good example of how to use the ``ci.json`` file: diff --git a/docs/en/guides/core_compatibility.rst b/docs/en/guides/core_compatibility.rst index 8b6a8e79d26..a440b21e497 100644 --- a/docs/en/guides/core_compatibility.rst +++ b/docs/en/guides/core_compatibility.rst @@ -26,7 +26,7 @@ To ensure compatibility with both versions of the ESP32 Arduino core, developers Version Print ------------- -To easily print the ESP32 Arduino core version at runtime, developers can use the `ESP_ARDUINO_VERSION_STR` macro. Below is an example of how to print the ESP32 Arduino core version: +To easily print the ESP32 Arduino core version at runtime, developers can use the ``ESP_ARDUINO_VERSION_STR`` macro. Below is an example of how to print the ESP32 Arduino core version: .. code-block:: cpp @@ -35,10 +35,10 @@ To easily print the ESP32 Arduino core version at runtime, developers can use th API Differences --------------- -Developers should be aware, that there may be API differences between major versions of the ESP32 Arduino core. For this we created a `Migration guide `_. to help developers transition from between major versions of the ESP32 Arduino core. +Developers should be aware, that there may be API differences between major versions of the ESP32 Arduino core. For this we created a `Migration guide `_. to help developers transition from between major versions of the ESP32 Arduino core. Library Testing --------------- We have added an External Library Test CI job, which tests external libraries with the latest version of the ESP32 Arduino core to help developers ensure compatibility with the latest version of the ESP32 Arduino core. -If you want to include your library in the External Library Test CI job, please follow the instructions in the `External Libraries Test `_. +If you want to include your library in the External Library Test CI job, please follow the instructions in the `External Libraries Test `_. diff --git a/docs/en/zigbee/ep_binary.rst b/docs/en/zigbee/ep_binary.rst index 950e20ef42b..06d65d05416 100644 --- a/docs/en/zigbee/ep_binary.rst +++ b/docs/en/zigbee/ep_binary.rst @@ -7,10 +7,7 @@ About The ``ZigbeeBinary`` class provides an endpoint for binary input/output sensors in Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for binary sensors, supporting various application types for HVAC, security, and general binary sensing. Binary Input (BI) is meant to be used for sensors that provide a binary signal, such as door/window sensors, motion detectors, etc. to be sent to the network. - -.. note:: - - Binary Output (BO) is not supported yet. +Binary Output (BO) is used for controlling binary devices such as relays, switches, or actuators that can be turned on/off remotely. API Reference ------------- @@ -63,6 +60,33 @@ Security Application Types #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF +Binary Output Application Types +******************************* + +HVAC Application Types +^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_BOILER 0x00000003 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_CHILLER 0x0000000D + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN 0x00000022 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HEATING_VALVE 0x0000002C + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER 0x00000033 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_PREHEAT 0x00000034 + #define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF + +Security Application Types +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: arduino + + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ARM_DISARM_COMMAND 0x01000000 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OCCUPANCY_CONTROL 0x01000001 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ENABLE_CONTROL 0x01000002 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ACCESS_CONTROL 0x01000003 + #define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF + API Methods *********** @@ -127,11 +151,94 @@ Manually reports the current binary input value. This function will return ``true`` if successful, ``false`` otherwise. +addBinaryOutput +^^^^^^^^^^^^^^^ + +Adds a binary output cluster to the endpoint. + +.. code-block:: arduino + + bool addBinaryOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +setBinaryOutputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the binary output. + +.. code-block:: arduino + + bool setBinaryOutputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Binary Output Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setBinaryOutputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the binary output. + +.. code-block:: arduino + + bool setBinaryOutputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +onBinaryOutputChange +^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when the binary output value changes. + +.. code-block:: arduino + + void onBinaryOutputChange(void (*callback)(bool binary_output)); + +* ``callback`` - Function pointer to callback that receives the new binary output value + +setBinaryOutput +^^^^^^^^^^^^^^^ + +Sets the binary output value. + +.. code-block:: arduino + + bool setBinaryOutput(bool output); + +* ``output`` - Binary value (true/false) + +This function will return ``true`` if successful, ``false`` otherwise. + +getBinaryOutput +^^^^^^^^^^^^^^^ + +Gets the current binary output value. + +.. code-block:: arduino + + bool getBinaryOutput(); + +This function returns the current binary output state. + +reportBinaryOutput +^^^^^^^^^^^^^^^^^^ + +Manually reports the current binary output value. + +.. code-block:: arduino + + bool reportBinaryOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + Example ------- -Binary Input Implementation -**************************** +Binary Input and Output Implementation +************************************** -.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino +.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino :language: arduino diff --git a/docs/en/zigbee/ep_multistate.rst b/docs/en/zigbee/ep_multistate.rst new file mode 100644 index 00000000000..004aeaf5660 --- /dev/null +++ b/docs/en/zigbee/ep_multistate.rst @@ -0,0 +1,346 @@ +################ +ZigbeeMultistate +################ + +About +----- + +The ``ZigbeeMultistate`` class provides multistate input and output endpoints for Zigbee networks. This endpoint implements the Zigbee Home Automation (HA) standard for multistate signal processing and control. +Multistate Input (MI) is meant to be used for sensors that provide discrete state values, such as fan speed settings, HVAC modes, or security states to be sent to the coordinator. +Multistate Output (MO) is meant to be used for actuators that require discrete state control, such as fan controllers, HVAC systems, or security devices to be controlled by the coordinator. + +.. note:: + + HomeAssistant ZHA does not support multistate input and output clusters yet. + +Common API +---------- + +Constructor +*********** + +ZigbeeMultistate +^^^^^^^^^^^^^^^^ + +Creates a new Zigbee multistate endpoint. + +.. code-block:: arduino + + ZigbeeMultistate(uint8_t endpoint); + +* ``endpoint`` - Endpoint number (1-254) + +Multistate Application Types +**************************** + +The ZigbeeMultistate class supports predefined application types with specific state configurations: + +.. code-block:: arduino + + // Application Type 0: Fan states (Off, On, Auto) + #define ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX 0x0000 + #define ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES (const char* const[]){"Off", "On", "Auto"} + + // Application Type 1: Fan speed states (Off, Low, Medium, High) + #define ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX 0x0001 + #define ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES (const char* const[]){"Off", "Low", "Medium", "High"} + + // Application Type 2: HVAC modes (Auto, Heat, Cool, Off, Emergency Heat, Fan Only, Max Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX 0x0002 + #define ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES 7 + #define ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES (const char* const[]){"Auto", "Heat", "Cool", "Off", "Emergency Heat", "Fan Only", "Max Heat"} + + // Application Type 3: Occupancy states (Occupied, Unoccupied, Standby, Bypass) + #define ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX 0x0003 + #define ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES (const char* const[]){"Occupied", "Unoccupied", "Standby", "Bypass"} + + // Application Type 4: Control states (Inactive, Active, Hold) + #define ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX 0x0004 + #define ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES (const char* const[]){"Inactive", "Active", "Hold"} + + // Application Type 5: Water system states (Auto, Warm-up, Water Flush, Autocalibration, Shutdown Open, Shutdown Closed, Low Limit, Test and Balance) + #define ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX 0x0005 + #define ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES 8 + #define ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES (const char* const[]){"Auto", "Warm-up", "Water Flush", "Autocalibration", "Shutdown Open", "Shutdown Closed", "Low Limit", "Test and Balance"} + + // Application Type 6: HVAC system states (Off, Auto, Heat Cool, Heat Only, Cool Only, Fan Only) + #define ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX 0x0006 + #define ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES 6 + #define ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES (const char* const[]){"Off", "Auto", "Heat Cool", "Heat Only", "Cool Only", "Fan Only"} + + // Application Type 7: Light states (High, Normal, Low) + #define ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX 0x0007 + #define ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES (const char* const[]){"High", "Normal", "Low"} + + // Application Type 8: Occupancy control states (Occupied, Unoccupied, Startup, Shutdown) + #define ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX 0x0008 + #define ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES 4 + #define ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES (const char* const[]){"Occupied", "Unoccupied", "Startup", "Shutdown"} + + // Application Type 9: Time states (Night, Day, Hold) + #define ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX 0x0009 + #define ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES 3 + #define ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES (const char* const[]){"Night", "Day", "Hold"} + + // Application Type 10: HVAC control states (Off, Cool, Heat, Auto, Emergency Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX 0x000A + #define ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES 5 + #define ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES (const char* const[]){"Off", "Cool", "Heat", "Auto", "Emergency Heat"} + + // Application Type 11: Water control states (Shutdown Closed, Shutdown Open, Satisfied, Mixing, Cooling, Heating, Suppl Heat) + #define ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX 0x000B + #define ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES 7 + #define ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES (const char* const[]){"Shutdown Closed", "Shutdown Open", "Satisfied", "Mixing", "Cooling", "Heating", "Suppl Heat"} + + // Custom application type for user-defined states + #define ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX 0xFFFF + +Cluster Management +****************** + +addMultistateInput +^^^^^^^^^^^^^^^^^^ + +Adds multistate input cluster to the endpoint. + +.. code-block:: arduino + + bool addMultistateInput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +addMultistateOutput +^^^^^^^^^^^^^^^^^^^ + +Adds multistate output cluster to the endpoint. + +.. code-block:: arduino + + bool addMultistateOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Multistate Input API +-------------------- + +Configuration Methods +********************* + +setMultistateInputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Multistate Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateInputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateInputStates +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the number of states for the multistate input. + +.. code-block:: arduino + + bool setMultistateInputStates(uint16_t number_of_states); + +* ``number_of_states`` - Number of discrete states (1-65535) + +This function will return ``true`` if successful, ``false`` otherwise. + +Value Control +************* + +setMultistateInput +^^^^^^^^^^^^^^^^^^ + +Sets the multistate input value. + +.. code-block:: arduino + + bool setMultistateInput(uint16_t state); + +* ``state`` - State index (0 to number_of_states-1) + +This function will return ``true`` if successful, ``false`` otherwise. + +getMultistateInput +^^^^^^^^^^^^^^^^^^ + +Gets the current multistate input value. + +.. code-block:: arduino + + uint16_t getMultistateInput(); + +This function returns the current multistate input state. + +Reporting Methods +***************** + +reportMultistateInput +^^^^^^^^^^^^^^^^^^^^^ + +Manually reports the current multistate input value. + +.. code-block:: arduino + + bool reportMultistateInput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Multistate Output API +--------------------- + +Configuration Methods +********************* + +setMultistateOutputApplication +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the application type for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputApplication(uint32_t application_type); + +* ``application_type`` - Application type constant (see Multistate Application Types above) + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateOutputDescription +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a custom description for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputDescription(const char *description); + +* ``description`` - Description string + +This function will return ``true`` if successful, ``false`` otherwise. + +setMultistateOutputStates +^^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets the number of states for the multistate output. + +.. code-block:: arduino + + bool setMultistateOutputStates(uint16_t number_of_states); + +* ``number_of_states`` - Number of discrete states (1-65535) + +This function will return ``true`` if successful, ``false`` otherwise. + +Value Control +************* + +setMultistateOutput +^^^^^^^^^^^^^^^^^^^^ + +Sets the multistate output value. + +.. code-block:: arduino + + bool setMultistateOutput(uint16_t state); + +* ``state`` - State index (0 to number_of_states-1) + +This function will return ``true`` if successful, ``false`` otherwise. + +getMultistateOutput +^^^^^^^^^^^^^^^^^^^ + +Gets the current multistate output value. + +.. code-block:: arduino + + uint16_t getMultistateOutput(); + +This function returns the current multistate output state. + +State Information +***************** + +getMultistateInputStateNamesLength +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of state names for multistate input. + +.. code-block:: arduino + + uint16_t getMultistateInputStateNamesLength(); + +This function returns the number of state names configured for multistate input. + +getMultistateOutputStateNamesLength +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Gets the number of state names for multistate output. + +.. code-block:: arduino + + uint16_t getMultistateOutputStateNamesLength(); + +This function returns the number of state names configured for multistate output. + +Reporting Methods +***************** + +reportMultistateOutput +^^^^^^^^^^^^^^^^^^^^^^^ + +Manually reports the current multistate output value. + +.. code-block:: arduino + + bool reportMultistateOutput(); + +This function will return ``true`` if successful, ``false`` otherwise. + +Event Handling +************** + +onMultistateOutputChange +^^^^^^^^^^^^^^^^^^^^^^^^ + +Sets a callback function to be called when the multistate output value changes. + +.. code-block:: arduino + + void onMultistateOutputChange(void (*callback)(uint16_t state)); + +* ``callback`` - Function to call when multistate output changes, receives the new state value + +Example +------- + +Multistate Input/Output +*********************** + +.. literalinclude:: ../../../libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino + :language: arduino diff --git a/docs/en/zigbee/zigbee_core.rst b/docs/en/zigbee/zigbee_core.rst index 89cf88ecca1..869af3007df 100644 --- a/docs/en/zigbee/zigbee_core.rst +++ b/docs/en/zigbee/zigbee_core.rst @@ -51,6 +51,24 @@ This function will return ``true`` if initialization successful, ``false`` other * Zigbee mode to ``Zigbee ED (end device)``. * Partition scheme to ``Zigbee xMB with spiffs`` (where ``x`` is the number of MB of selected flash size). +start +^^^^^ +Starts the Zigbee stack again, if it was stopped by calling ``stop()``. + +.. code-block:: arduino + + void start(); + + +stop +^^^^ +Stops the Zigbee stack. This can be used after calling ``begin()`` to stop the Zigbee stack. +Usage example is to save power or when you need the radio to be available for other tasks. + +.. code-block:: arduino + + void stop(); + Network Status ************** diff --git a/idf_component.yml b/idf_component.yml index 82f14ea554a..9cbe8aac52c 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -54,12 +54,12 @@ dependencies: espressif/esp_modem: version: "^1.1.0" espressif/esp-zboss-lib: - version: "==1.6.4" # compatible with esp-zigbee-lib 1.6.5 + version: "==1.6.4" # compatible with esp-zigbee-lib 1.6.7 require: public rules: - if: "target not in [esp32c2, esp32p4]" espressif/esp-zigbee-lib: - version: "==1.6.5" + version: "==1.6.7" require: public rules: - if: "target not in [esp32c2, esp32p4]" @@ -103,9 +103,9 @@ dependencies: - if: "target not in [esp32c2, esp32p4]" # RainMaker End espressif/esp-sr: - version: "^1.4.2" + version: "^2.1.5" rules: - - if: "target in [esp32s3]" + - if: "target in [esp32s3, esp32p4]" espressif/esp_hosted: version: "^2.0.12" rules: @@ -125,6 +125,10 @@ dependencies: chmorgan/esp-libhelix-mp3: version: "1.0.3" require: public + espressif/lan867x: + version: "^2.0.0" + rules: + - if: "target in [esp32, esp32p4]" examples: - path: ./idf_component_examples/hello_world - path: ./idf_component_examples/hw_cdc_hello_world diff --git a/idf_component_examples/esp_matter_light/sdkconfig.defaults b/idf_component_examples/esp_matter_light/sdkconfig.defaults index 43871661856..8688318fa36 100644 --- a/idf_component_examples/esp_matter_light/sdkconfig.defaults +++ b/idf_component_examples/esp_matter_light/sdkconfig.defaults @@ -60,3 +60,8 @@ 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/hello_world/sdkconfig.defaults b/idf_component_examples/hello_world/sdkconfig.defaults index bb723653f8a..a604d9767fd 100644 --- a/idf_component_examples/hello_world/sdkconfig.defaults +++ b/idf_component_examples/hello_world/sdkconfig.defaults @@ -10,3 +10,8 @@ CONFIG_AUTOSTART_ARDUINO=y CONFIG_FREERTOS_HZ=1000 # end of FREERTOS # end of Component config + +# +# DIAGNOSTICS +# +CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y diff --git a/idf_component_examples/hw_cdc_hello_world/sdkconfig.defaults b/idf_component_examples/hw_cdc_hello_world/sdkconfig.defaults index bb723653f8a..a604d9767fd 100644 --- a/idf_component_examples/hw_cdc_hello_world/sdkconfig.defaults +++ b/idf_component_examples/hw_cdc_hello_world/sdkconfig.defaults @@ -10,3 +10,8 @@ CONFIG_AUTOSTART_ARDUINO=y CONFIG_FREERTOS_HZ=1000 # end of FREERTOS # end of Component config + +# +# DIAGNOSTICS +# +CONFIG_DIAG_USE_EXTERNAL_LOG_WRAP=y diff --git a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino index b3b01be61cd..934789a52bf 100644 --- a/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino +++ b/libraries/ArduinoOTA/examples/BasicOTA/BasicOTA.ino @@ -19,6 +19,7 @@ const char *ssid = ".........."; const char *password = ".........."; +uint32_t last_ota_time = 0; void setup() { Serial.begin(115200); @@ -40,9 +41,13 @@ void setup() { // No authentication by default // ArduinoOTA.setPassword("admin"); - // Password can be set with it's md5 value as well - // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3 - // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3"); + // Password can be set with plain text (will be hashed internally) + // The authentication uses PBKDF2-HMAC-SHA256 with 10,000 iterations + // ArduinoOTA.setPassword("admin"); + + // Or set password with pre-hashed value (SHA256 hash of "admin") + // SHA256(admin) = 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 + // ArduinoOTA.setPasswordHash("8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918"); ArduinoOTA .onStart([]() { @@ -60,7 +65,10 @@ void setup() { Serial.println("\nEnd"); }) .onProgress([](unsigned int progress, unsigned int total) { - Serial.printf("Progress: %u%%\r", (progress / (total / 100))); + if (millis() - last_ota_time > 500) { + Serial.printf("Progress: %u%%\n", (progress / (total / 100))); + last_ota_time = millis(); + } }) .onError([](ota_error_t error) { Serial.printf("Error[%u]: ", error); diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.cpp b/libraries/ArduinoOTA/src/ArduinoOTA.cpp index cb3ddc1e797..308660c3ce7 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.cpp +++ b/libraries/ArduinoOTA/src/ArduinoOTA.cpp @@ -19,7 +19,8 @@ #include "ArduinoOTA.h" #include "NetworkClient.h" #include "ESPmDNS.h" -#include "MD5Builder.h" +#include "SHA2Builder.h" +#include "PBKDF2_HMACBuilder.h" #include "Update.h" // #define OTA_DEBUG Serial @@ -72,18 +73,20 @@ String ArduinoOTAClass::getHostname() { ArduinoOTAClass &ArduinoOTAClass::setPassword(const char *password) { if (_state == OTA_IDLE && password) { - MD5Builder passmd5; - passmd5.begin(); - passmd5.add(password); - passmd5.calculate(); + // Hash the password with SHA256 for storage (not plain text) + SHA256Builder pass_hash; + pass_hash.begin(); + pass_hash.add(password); + pass_hash.calculate(); _password.clear(); - _password = passmd5.toString(); + _password = pass_hash.toString(); } return *this; } ArduinoOTAClass &ArduinoOTAClass::setPasswordHash(const char *password) { if (_state == OTA_IDLE && password) { + // Store the pre-hashed password directly _password.clear(); _password = password; } @@ -188,17 +191,18 @@ void ArduinoOTAClass::_onRx() { _udp_ota.read(); _md5 = readStringUntil('\n'); _md5.trim(); - if (_md5.length() != 32) { + if (_md5.length() != 32) { // MD5 produces 32 character hex string for firmware integrity log_e("bad md5 length"); return; } if (_password.length()) { - MD5Builder nonce_md5; - nonce_md5.begin(); - nonce_md5.add(String(micros())); - nonce_md5.calculate(); - _nonce = nonce_md5.toString(); + // Generate a random challenge (nonce) + SHA256Builder nonce_sha256; + nonce_sha256.begin(); + nonce_sha256.add(String(micros()) + String(random(1000000))); + nonce_sha256.calculate(); + _nonce = nonce_sha256.toString(); _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); _udp_ota.printf("AUTH %s", _nonce.c_str()); @@ -222,20 +226,37 @@ void ArduinoOTAClass::_onRx() { _udp_ota.read(); String cnonce = readStringUntil(' '); String response = readStringUntil('\n'); - if (cnonce.length() != 32 || response.length() != 32) { + if (cnonce.length() != 64 || response.length() != 64) { // SHA256 produces 64 character hex string log_e("auth param fail"); _state = OTA_IDLE; return; } - String challenge = _password + ":" + String(_nonce) + ":" + cnonce; - MD5Builder _challengemd5; - _challengemd5.begin(); - _challengemd5.add(challenge); - _challengemd5.calculate(); - String result = _challengemd5.toString(); - - if (result.equals(response)) { + // Verify the challenge/response using PBKDF2-HMAC-SHA256 + // The client should derive a key using PBKDF2-HMAC-SHA256 with: + // - password: the OTA password (or its hash if using setPasswordHash) + // - salt: nonce + cnonce + // - iterations: 10000 (or configurable) + // Then hash the challenge with the derived key + + String salt = _nonce + ":" + cnonce; + SHA256Builder sha256; + // Use the stored password hash for PBKDF2 derivation + PBKDF2_HMACBuilder pbkdf2(&sha256, _password, salt, 10000); + + pbkdf2.begin(); + pbkdf2.calculate(); + String derived_key = pbkdf2.toString(); + + // Create challenge: derived_key + nonce + cnonce + String challenge = derived_key + ":" + _nonce + ":" + cnonce; + SHA256Builder challenge_sha256; + challenge_sha256.begin(); + challenge_sha256.add(challenge); + challenge_sha256.calculate(); + String expected_response = challenge_sha256.toString(); + + if (expected_response.equals(response)) { _udp_ota.beginPacket(_udp_ota.remoteIP(), _udp_ota.remotePort()); _udp_ota.print("OK"); _udp_ota.endPacket(); @@ -266,7 +287,8 @@ void ArduinoOTAClass::_runUpdate() { _state = OTA_IDLE; return; } - Update.setMD5(_md5.c_str()); + + Update.setMD5(_md5.c_str()); // Note: Update library still uses MD5 for firmware integrity, this is separate from authentication if (_start_callback) { _start_callback(); diff --git a/libraries/ArduinoOTA/src/ArduinoOTA.h b/libraries/ArduinoOTA/src/ArduinoOTA.h index 7916e3b328d..a946388c4aa 100644 --- a/libraries/ArduinoOTA/src/ArduinoOTA.h +++ b/libraries/ArduinoOTA/src/ArduinoOTA.h @@ -54,7 +54,7 @@ class ArduinoOTAClass { //Sets the password that will be required for OTA. Default NULL ArduinoOTAClass &setPassword(const char *password); - //Sets the password as above but in the form MD5(password). Default NULL + //Sets the password as above but in the form SHA256(password). Default NULL ArduinoOTAClass &setPasswordHash(const char *password); //Sets the partition label to write to when updating SPIFFS. Default NULL diff --git a/libraries/AsyncUDP/src/AsyncUDP.cpp b/libraries/AsyncUDP/src/AsyncUDP.cpp index 2d533831cd5..decd35106ec 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.cpp +++ b/libraries/AsyncUDP/src/AsyncUDP.cpp @@ -317,6 +317,33 @@ AsyncUDPPacket::AsyncUDPPacket(AsyncUDPPacket &packet) { pbuf_ref(_pb); } +AsyncUDPPacket &AsyncUDPPacket::operator=(const AsyncUDPPacket &packet) { + if (this != &packet) { + if (_pb) { + // Free existing pbuf reference + pbuf_free(_pb); + } + + // Copy all members + _udp = packet._udp; + _pb = packet._pb; + _if = packet._if; + _data = packet._data; + _len = packet._len; + _index = 0; + + memcpy(&_remoteIp, &packet._remoteIp, sizeof(ip_addr_t)); + memcpy(&_localIp, &packet._localIp, sizeof(ip_addr_t)); + _localPort = packet._localPort; + _remotePort = packet._remotePort; + memcpy(_remoteMac, packet._remoteMac, 6); + + // Increment reference count for the new pbuf + pbuf_ref(_pb); + } + return *this; +} + AsyncUDPPacket::AsyncUDPPacket(AsyncUDP *udp, pbuf *pb, const ip_addr_t *raddr, uint16_t rport, struct netif *ntif) { _udp = udp; _pb = pb; diff --git a/libraries/AsyncUDP/src/AsyncUDP.h b/libraries/AsyncUDP/src/AsyncUDP.h index cd96d852542..9f4778888f0 100644 --- a/libraries/AsyncUDP/src/AsyncUDP.h +++ b/libraries/AsyncUDP/src/AsyncUDP.h @@ -100,6 +100,9 @@ class AsyncUDPPacket : public Stream { size_t write(const uint8_t *data, size_t len); size_t write(uint8_t data); + + // Copy assignment operator + AsyncUDPPacket &operator=(const AsyncUDPPacket &packet); }; class AsyncUDP : public Print { diff --git a/libraries/BLE/README.md b/libraries/BLE/README.md index 759c8526f0f..b89d556dcc9 100644 --- a/libraries/BLE/README.md +++ b/libraries/BLE/README.md @@ -1,12 +1,467 @@ -# ESP32 BLE for Arduino -The Arduino IDE provides an excellent library package manager where versions of libraries can be downloaded and installed. This Github project provides the repository for the ESP32 BLE support for Arduino. +# BLE for ESP32 Arduino Core + +A comprehensive reference for ESP32 Bluetooth Low Energy (BLE) pairing and security implementation using the ESP32 Arduino BLE library. + +## Overview + +This guide provides ESP32 developers with comprehensive information about BLE security implementation using the ESP32 Arduino BLE library. It covers both Bluedroid (ESP32) and NimBLE (other SoCs) implementations with realistic scenarios and troubleshooting guidance. + +Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues
(please don't use https://github.com/nkolban/esp32-snippets/issues or https://github.com/h2zero/NimBLE-Arduino/issues) + +## Security + +### Quick Start + +1. **Choose your ESP32's IO capabilities** using `ESP_IO_CAP_*` constants +2. **Configure authentication requirements** with properties or permissions +3. **Set up security** using `BLESecurity` class methods +4. **Handle stack differences** between Bluedroid (ESP32) and NimBLE (other SoCs) +5. **Test with NVS clearing** during development + +### Understanding BLE Pairing + +#### Pairing vs Bonding +- **Pairing**: The process of establishing encryption keys between devices +- **Bonding**: Storing those keys for future reconnections (persistent pairing) + +#### Pairing Types +- **Legacy Pairing**: Original BLE pairing (Bluetooth 4.0/4.1) +- **Secure Connections**: Enhanced security (Bluetooth 4.2+) using FIPS-approved algorithms + +#### Security Levels +- **Just Works**: Encryption without user verification (vulnerable to passive eavesdropping) +- **MITM Protected**: User verification prevents man-in-the-middle attacks + +### IO Capabilities Explained + +The ESP32 BLE library defines the following IO capabilities: + +| Capability | Library Constant | Can Display | Can Input | Can Confirm | Example Devices | +|------------|-----------------|-------------|-----------|-------------|-----------------| +| **No Input No Output** | `ESP_IO_CAP_NONE` | ❌ | ❌ | ❌ | Sensor nodes, beacons, simple actuators | +| **Display Only** | `ESP_IO_CAP_OUT` | ✅ | ❌ | ❌ | E-ink displays, LED matrix displays | +| **Keyboard Only** | `ESP_IO_CAP_IN` | ❌ | ✅ | ❌ | Button-only devices, rotary encoders | +| **Display Yes/No** | `ESP_IO_CAP_IO` | ✅ | ❌ | ✅ | Devices with display + confirmation button | +| **Keyboard Display** | `ESP_IO_CAP_KBDISP` | ✅ | ✅ | ✅ | Full-featured ESP32 devices with UI | + +### Pairing Methods Explained + +#### 🔓 Just Works +- **Security**: Encryption only (no MITM protection) +- **User Experience**: Automatic, no user interaction +- **Use Case**: Convenience over security (fitness trackers, mice) +- **Vulnerability**: Susceptible to passive eavesdropping during pairing + +#### 🔐 Passkey Entry +- **Security**: Full MITM protection +- **User Experience**: One device shows 6-digit code, other inputs it +- **Use Case**: Keyboards pairing to computers +- **Process**: + 1. Display device shows random 6-digit number (000000-999999) + 2. Input device user types the number + 3. Pairing succeeds if numbers match + +#### 🔐 Numeric Comparison (Secure Connections Only) +- **Security**: Full MITM protection +- **User Experience**: Both devices show same number, user confirms match +- **Use Case**: Two smartphones/tablets pairing +- **Process**: + 1. Both devices display identical 6-digit number + 2. User verifies numbers match on both screens + 3. User confirms "Yes" on both devices + +#### 🔐 Out-of-Band (OOB) (Not supported by this library) +- **Security**: Highest security level +- **User Experience**: Uses external channel (NFC, QR code) +- **Use Case**: High-security applications +- **Priority**: Always used when OOB data is available + +### Pairing Methods Compatibility Matrix + +Here is the compatibility matrix for the pairing methods depending on the IO capabilities of the devices. +Note that the initiator is the device that starts the pairing process (usually the client) and the responder is +the device that accepts the pairing request (usually the server). + +![Pairing Methods Compatibility Matrix](https://www.bluetooth.com/wp-content/uploads/2016/06/screen-shot-06-08-16-at-0124-pm.png) + +### Bluedroid vs NimBLE + +Bluedroid and NimBLE are two different Bluetooth stack implementations. + +#### Bluedroid + +Bluedroid is the default Bluetooth stack in ESP-IDF. It supports both Bluetooth Classic and Bluetooth LE. It is used by the ESP32 in the Arduino Core. + +Bluedroid requires more flash and RAM than NimBLE and access permissions for characteristics and descriptors are set using a specific API through the `setAccessPermissions()` function. The original source of the Bluedroid project, **which is not maintained anymore**, can be found here: https://github.com/nkolban/esp32-snippets -Some parts of the NimBLE implementation are based on the work of h2zero, which can be found here: https://github.com/h2zero/NimBLE-Arduino +**Bluedroid will be replaced by NimBLE in version 4.0.0 of the Arduino Core. Bluetooth Classic and Bluedroid will no longer be supported but can be used by using Arduino as an ESP-IDF component.** + +#### NimBLE + +NimBLE is a lightweight Bluetooth stack for Bluetooth LE only. It is used by all SoCs that are not the ESP32. + +NimBLE requires less flash and RAM than Bluedroid. Access permissions for characteristics are set using exclusive properties in the characteristic creation. Access permissions for descriptors are set using the `setAccessPermissions()` function just like in Bluedroid. + +Some parts of the NimBLE implementation are based on the work of h2zero, which can be found here: https://github.com/h2zero/NimBLE-Arduino. For a more customizable and feature-rich implementation of the NimBLE stack, you can use the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. + +### Common Scenarios + +Here are some common scenarios for the pairing methods depending on the IO capabilities of the devices. Check also the secure BLE examples in the ESP32 Arduino Core for more detailed usage examples. + +#### Scenario 1: Mobile App ↔ ESP32 Sensor Node +- **Devices**: Mobile (`ESP_IO_CAP_IO`) ↔ ESP32 Sensor (`ESP_IO_CAP_NONE`) +- **MITM**: Not achievable with this IO combination (falls back to Just Works) +- **Characteristic Authentication**: Bonding only +- **Result**: Just Works with bonding for reconnection +- **Use Case**: Weather stations, environmental monitors + +#### Scenario 2: ESP32 Smart Lock ↔ Mobile App +- **Devices**: ESP32 Lock (`ESP_IO_CAP_OUT`) ↔ Mobile (`ESP_IO_CAP_KBDISP`) +- **MITM**: Required for security +- **Characteristic Authentication**: Bonding + Secure Connection + MITM +- **Result**: Passkey Entry (ESP32 displays, mobile enters) +- **Implementation**: Static passkey or dynamic display + +#### Scenario 3: ESP32 Configuration Device ↔ Admin Tool +- **Devices**: ESP32 (`ESP_IO_CAP_KBDISP`) ↔ Admin Tool (`ESP_IO_CAP_KBDISP`) +- **MITM**: Required for configuration security +- **Characteristic Authentication**: Bonding + Secure Connection + MITM +- **Result**: + - Legacy: Passkey Entry + - Secure Connections: Numeric Comparison +- **Use Case**: Industrial IoT configuration, network setup + +#### Scenario 4: ESP32 Beacon ↔ Scanner App +- **Devices**: ESP32 Beacon (`ESP_IO_CAP_NONE`) ↔ Scanner (`ESP_IO_CAP_IO`) +- **MITM**: Not required (broadcast only) +- **Characteristic Authentication**: None +- **Result**: No pairing required +- **Use Case**: Asset tracking, proximity detection + +#### Scenario 5: ESP32 Smart Home Hub ↔ Multiple Devices +- **Devices**: ESP32 Hub (`ESP_IO_CAP_IO`) ↔ Various sensors (`ESP_IO_CAP_NONE`) +- **MITM**: Not possible when any of the peers are `ESP_IO_CAP_NONE` +- **Characteristic Authentication**: Bonding only (no MITM possible with `ESP_IO_CAP_NONE`) +- **Result**: Just Works only +- **Use Case**: Centralized home automation controller + +### Implementation Guidelines + +#### For ESP32 Device Developers + +##### Choosing IO Capabilities +```cpp +#include + +// Conservative approach - limits pairing methods but ensures compatibility +pSecurity->setCapability(ESP_IO_CAP_NONE); // Just Works only + +// Balanced approach - good UX with optional security +pSecurity->setCapability(ESP_IO_CAP_IO); // Just Works or Numeric Comparison + +// Maximum security - supports all methods +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // All pairing methods available +``` + +##### Authentication Configuration +```cpp +BLESecurity *pSecurity = new BLESecurity(); + +// Low security applications (sensors, environmental monitoring) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_NO_BOND); + +// Standard security with bonding (smart home devices) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_BOND); + +// MITM protection required (access control, payments) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_MITM | ESP_LE_AUTH_BOND); + +// Maximum security with Secure Connections (critical systems) +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); + +// Alternative syntax (more readable for complex requirements) +pSecurity->setAuthenticationMode(true, true, true); // Bonding, MITM, Secure Connections +``` + +##### Static Passkey Example (from secure examples) +```cpp +// Set a static passkey for consistent pairing experience +#define DEVICE_PASSKEY 123456 +pSecurity->setPassKey(true, DEVICE_PASSKEY); // static=true, passkey=123456 +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // Required for MITM even with static passkey +``` + +### Security Considerations + +#### When to Require MITM +- **Always**: Payment systems, medical devices, access control +- **Usually**: File transfers, personal data sync, keyboards +- **Optional**: Fitness trackers, environmental sensors, mice +- **Never**: Beacons, broadcast-only devices + +#### Legacy vs Secure Connections +- **Legacy**: Compatible with all BLE devices (2010+) +- **Secure Connections**: Better security but requires Bluetooth 4.2+ (2014+) +- **Recommendation**: Support both, prefer Secure Connections when available + +#### Implementation Differences +```cpp +// Basic characteristic properties (both stacks) +uint32_t properties = BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE; + +// NimBLE: Add authentication properties (ignored by Bluedroid) +properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_WRITE_AUTHEN; + +BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHAR_UUID, properties); + +// Bluedroid: Set access permissions (ignored by NimBLE) +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); + +// Check which stack is running +String stackType = BLEDevice::getBLEStackString(); +Serial.println("Using BLE stack: " + stackType); +``` + +#### Known Vulnerabilities +1. **Just Works**: Vulnerable to passive eavesdropping during initial pairing +2. **Legacy Pairing**: Uses weaker cryptographic algorithms +3. **Passkey Brute Force**: 6-digit passkeys have only 1M combinations +4. **Physical Security**: Displayed passkeys can be shoulder-surfed + +### Troubleshooting + +#### Common Issues + +##### Pairing Always Uses Just Works +```cpp +// ❌ Problem: Missing MITM flag +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_BOND); + +// ✅ Solution: Add MITM protection +pSecurity->setAuthenticationMode(ESP_LE_AUTH_REQ_SC_MITM_BOND); +``` + +##### Static passkey not being requested / Nothing happens when trying to read secure characteristic +```cpp +// ❌ Problem: Wrong IO capability for MITM +pSecurity->setCapability(ESP_IO_CAP_NONE); // Can't support MITM + +// ✅ Solution: Set proper capability even for static passkey +pSecurity->setCapability(ESP_IO_CAP_KBDISP); // Required for MITM +pSecurity->setPassKey(true, 123456); +``` + +##### Secure Characteristic Access Fails +```cpp +// ❌ Problem: Wrong security method for stack +// Bluedroid approach (won't work on NimBLE) +uint32_t properties = BLECharacteristic::PROPERTY_READ; +pCharacteristic = pService->createCharacteristic(uuid, properties); +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM); // Ignored by NimBLE! + +// ✅ Solution: Use both methods for cross-compatibility +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_READ_AUTHEN; // For NimBLE +pCharacteristic = pService->createCharacteristic(uuid, properties); +pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM); // For Bluedroid +``` + +##### Pairing Works Once, Then Fails (NVS Cache Issue) +```cpp +// ✅ Solution: Clear NVS for testing/development +Serial.println("Clearing NVS pairing data for testing..."); +nvs_flash_erase(); +nvs_flash_init(); +``` + +##### Default Passkey Warning +``` +*WARNING* Using default passkey: 123456 +*WARNING* Please use a random passkey or set a different static passkey +``` +```cpp +// ✅ Solution: Change from default +#define CUSTOM_PASSKEY 567890 // Your unique passkey +pSecurity->setPassKey(true, CUSTOM_PASSKEY); +``` + +##### Connection drops during pairing +```cpp +// ✅ Solution: Implement security callbacks for better error handling +class MySecurityCallbacks : public BLESecurityCallbacks { + void onAuthenticationComplete(esp_ble_auth_cmpl_t param) override { + if (param.success) { + Serial.println("Pairing successful!"); + } else { + Serial.printf("Pairing failed, reason: %d\n", param.fail_reason); + } + } +}; + +BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); +``` + +#### Cross-Platform Best Practice +```cpp +// Always use both methods for maximum compatibility +uint32_t secure_properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHEN | // NimBLE + BLECharacteristic::PROPERTY_WRITE_AUTHEN; // NimBLE + +BLECharacteristic *pChar = pService->createCharacteristic(uuid, secure_properties); + +// Bluedroid permissions (ignored by NimBLE, but doesn't hurt) +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); +``` + +### Complete Properties and Permissions Reference + +#### Bluedroid + +Bluedroid uses properties to define the capabilities of a characteristic and permissions to define the access permissions. NimBLE will ignore the access permissions. + +##### Supported Properties +```cpp +BLECharacteristic::PROPERTY_READ // Read operation +BLECharacteristic::PROPERTY_WRITE // Write operation +BLECharacteristic::PROPERTY_WRITE_NR // Write without response +BLECharacteristic::PROPERTY_NOTIFY // Notifications +BLECharacteristic::PROPERTY_INDICATE // Indications +BLECharacteristic::PROPERTY_BROADCAST // Broadcast +``` + +##### Characteristic and Descriptor Access Permissions +```cpp +// Basic permissions +ESP_GATT_PERM_READ // Read allowed +ESP_GATT_PERM_WRITE // Write allowed + +// Encryption required +ESP_GATT_PERM_READ_ENCRYPTED // Read requires encryption +ESP_GATT_PERM_WRITE_ENCRYPTED // Write requires encryption + +// Authentication required (MITM protection) +ESP_GATT_PERM_READ_ENC_MITM // Read requires encryption + MITM +ESP_GATT_PERM_WRITE_ENC_MITM // Write requires encryption + MITM + +// Authorization required +ESP_GATT_PERM_READ_AUTHORIZATION // Read requires authorization callback +ESP_GATT_PERM_WRITE_AUTHORIZATION // Write requires authorization callback +``` + +#### NimBLE + +NimBLE uses properties to define both the capabilities of a characteristic and the access permissions. Bluedroid will ignore the NimBLE exclusive properties. + +##### Supported Properties +```cpp +// Basic properties +BLECharacteristic::PROPERTY_READ // Read operation +BLECharacteristic::PROPERTY_WRITE // Write operation +BLECharacteristic::PROPERTY_WRITE_NR // Write without response +BLECharacteristic::PROPERTY_NOTIFY // Notifications +BLECharacteristic::PROPERTY_INDICATE // Indications +BLECharacteristic::PROPERTY_BROADCAST // Broadcast + +// NimBLE specific properties + +// Encryption required +BLECharacteristic::PROPERTY_READ_ENC // Read requires encryption +BLECharacteristic::PROPERTY_WRITE_ENC // Write requires encryption + +// Authentication required (MITM protection) +BLECharacteristic::PROPERTY_READ_AUTHEN // Read requires encryption + MITM protection +BLECharacteristic::PROPERTY_WRITE_AUTHEN // Write requires encryption + MITM protection + +// Authorization required +BLECharacteristic::PROPERTY_READ_AUTHOR // Read requires authorization callback +BLECharacteristic::PROPERTY_WRITE_AUTHOR // Write requires authorization callback +``` + +##### Descriptor Access Permissions +```cpp +// Basic permissions +ESP_GATT_PERM_READ // Read allowed +ESP_GATT_PERM_WRITE // Write allowed + +// Encryption required +ESP_GATT_PERM_READ_ENCRYPTED // Read requires encryption +ESP_GATT_PERM_WRITE_ENCRYPTED // Write requires encryption + +// Authentication required (MITM protection) +ESP_GATT_PERM_READ_ENC_MITM // Read requires encryption + MITM +ESP_GATT_PERM_WRITE_ENC_MITM // Write requires encryption + MITM + +// Authorization required +ESP_GATT_PERM_READ_AUTHORIZATION // Read requires authorization callback +ESP_GATT_PERM_WRITE_AUTHORIZATION // Write requires authorization callback +``` + +#### Usage Examples by Security Level + +##### No Security (Both Stacks) +```cpp +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +``` + +##### Encryption Only +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_ENC | + BLECharacteristic::PROPERTY_WRITE_ENC; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENCRYPTED | ESP_GATT_PERM_WRITE_ENCRYPTED); +``` + +##### MITM Protection (Authentication) +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHEN | + BLECharacteristic::PROPERTY_WRITE_AUTHEN; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); +``` + +##### Authorization Required +```cpp +// NimBLE +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE | + BLECharacteristic::PROPERTY_READ_AUTHOR | + BLECharacteristic::PROPERTY_WRITE_AUTHOR; + +// Bluedroid +uint32_t properties = BLECharacteristic::PROPERTY_READ | + BLECharacteristic::PROPERTY_WRITE; +pChar->setAccessPermissions(ESP_GATT_PERM_READ_AUTHORIZATION | ESP_GATT_PERM_WRITE_AUTHORIZATION); +``` + +#### Debug Tips +1. **Log pairing features** exchanged between devices +2. **Monitor pairing method** selected by the stack +3. **Check timeout values** for user input methods +4. **Verify key distribution** flags match on both sides + +### Standards References -Issues and questions should be raised here: https://github.com/espressif/arduino-esp32/issues
(please don't use https://github.com/nkolban/esp32-snippets/issues or https://github.com/h2zero/NimBLE-Arduino/issues!) +- **Bluetooth Core Specification v5.4**: Volume 3, Part H (Security Manager) +- **Bluetooth Assigned Numbers**: IO Capability values +- **FIPS-140-2**: Cryptographic standards for Secure Connections -Documentation for using the library can be found here: https://github.com/nkolban/esp32-snippets/tree/master/Documentation +--- -For a more customizable and feature-rich implementation of the NimBLE stack, you can use the [NimBLE-Arduino](https://github.com/h2zero/NimBLE-Arduino) library. +*This guide is based on the ESP32 Arduino BLE library implementation and the official Bluetooth Core Specification. For the latest API documentation, refer to the ESP32 Arduino BLE library source code and examples.* diff --git a/libraries/BLE/examples/Beacon_Scanner/ci.json b/libraries/BLE/examples/Beacon_Scanner/ci.json index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Beacon_Scanner/ci.json +++ b/libraries/BLE/examples/Beacon_Scanner/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Client/ci.json +++ b/libraries/BLE/examples/Client/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "requires_any": [ + "CONFIG_SOC_BLE_SUPPORTED=y", + "CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y" ] } diff --git a/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino b/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino new file mode 100644 index 00000000000..ec3c457221c --- /dev/null +++ b/libraries/BLE/examples/Client_secure_static_passkey/Client_secure_static_passkey.ino @@ -0,0 +1,248 @@ +/* + Secure client with static passkey + + 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. + + This client is designed to work with the Server_secure_static_passkey example. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + This means that in NimBLE you can read the insecure characteristic without entering + the passkey. This is not possible in Bluedroid. + + IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts + to work. Without MITM, the BLE stack assumes no user interaction is needed and will use + "Just Works" pairing method (with encryption if secure connection is enabled). + + Based on examples from Neil Kolban and h2zero. + Created by lucasssvaz. +*/ + +#include "BLEDevice.h" +#include "BLESecurity.h" +#include "nvs_flash.h" + +// The remote service we wish to connect to. +static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b"); +// The characteristics of the remote service we are interested in. +static BLEUUID insecureCharUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8"); +static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8"); + +// This must match the server's passkey +#define CLIENT_PIN 123456 + +static boolean doConnect = false; +static boolean connected = false; +static boolean doScan = false; +static BLERemoteCharacteristic *pRemoteInsecureCharacteristic; +static BLERemoteCharacteristic *pRemoteSecureCharacteristic; +static BLEAdvertisedDevice *myDevice; + +// Callback function to handle notifications +static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) { + Serial.print("Notify callback for characteristic "); + Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str()); + Serial.print(" of data length "); + Serial.println(length); + Serial.print("data: "); + Serial.write(pData, length); + Serial.println(); +} + +class MyClientCallback : public BLEClientCallbacks { + void onConnect(BLEClient *pclient) { + Serial.println("Connected to secure server"); + } + + void onDisconnect(BLEClient *pclient) { + connected = false; + Serial.println("Disconnected from server"); + } +}; + +bool connectToServer() { + Serial.print("Forming a secure connection to "); + Serial.println(myDevice->getAddress().toString().c_str()); + + BLEClient *pClient = BLEDevice::createClient(); + Serial.println(" - Created client"); + + pClient->setClientCallbacks(new MyClientCallback()); + + // Connect to the remote BLE Server. + pClient->connect(myDevice); + Serial.println(" - Connected to server"); + + // Set MTU to maximum for better performance + pClient->setMTU(517); + + // Obtain a reference to the service we are after in the remote BLE server. + BLERemoteService *pRemoteService = pClient->getService(serviceUUID); + if (pRemoteService == nullptr) { + Serial.print("Failed to find our service UUID: "); + Serial.println(serviceUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found our service"); + + // Obtain a reference to the insecure characteristic + pRemoteInsecureCharacteristic = pRemoteService->getCharacteristic(insecureCharUUID); + if (pRemoteInsecureCharacteristic == nullptr) { + Serial.print("Failed to find insecure characteristic UUID: "); + Serial.println(insecureCharUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found insecure characteristic"); + + // Obtain a reference to the secure characteristic + pRemoteSecureCharacteristic = pRemoteService->getCharacteristic(secureCharUUID); + if (pRemoteSecureCharacteristic == nullptr) { + Serial.print("Failed to find secure characteristic UUID: "); + Serial.println(secureCharUUID.toString().c_str()); + pClient->disconnect(); + return false; + } + Serial.println(" - Found secure characteristic"); + + // Read the value of the insecure characteristic (should work without authentication) + if (pRemoteInsecureCharacteristic->canRead()) { + String value = pRemoteInsecureCharacteristic->readValue(); + Serial.print("Insecure characteristic value: "); + Serial.println(value.c_str()); + } + + // For Bluedroid, we need to set the authentication request type for the secure characteristic + // This is not needed for NimBLE and will be ignored. + pRemoteSecureCharacteristic->setAuth(ESP_GATT_AUTH_REQ_MITM); + + // Try to read the secure characteristic (this will trigger security negotiation in NimBLE) + if (pRemoteSecureCharacteristic->canRead()) { + Serial.println("Attempting to read secure characteristic..."); + String value = pRemoteSecureCharacteristic->readValue(); + Serial.print("Secure characteristic value: "); + Serial.println(value.c_str()); + } + + // Register for notifications on both characteristics if they support it + if (pRemoteInsecureCharacteristic->canNotify()) { + pRemoteInsecureCharacteristic->registerForNotify(notifyCallback); + Serial.println(" - Registered for insecure characteristic notifications"); + } + + if (pRemoteSecureCharacteristic->canNotify()) { + pRemoteSecureCharacteristic->registerForNotify(notifyCallback); + Serial.println(" - Registered for secure characteristic notifications"); + } + + connected = true; + return true; +} + +/** + * Scan for BLE servers and find the first one that advertises the service we are looking for. + */ +class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks { + /** + * Called for each advertising BLE server. + */ + void onResult(BLEAdvertisedDevice advertisedDevice) { + Serial.print("BLE Advertised Device found: "); + Serial.println(advertisedDevice.toString().c_str()); + + // We have found a device, let us now see if it contains the service we are looking for. + if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) { + Serial.println("Found our secure server!"); + BLEDevice::getScan()->stop(); + myDevice = new BLEAdvertisedDevice(advertisedDevice); + doConnect = true; + doScan = true; + } + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting Secure BLE Client application..."); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + BLEDevice::init("Secure BLE Client"); + + // Set up security with the same passkey as the server + BLESecurity *pSecurity = new BLESecurity(); + + // Set security parameters + // Default parameters: + // - IO capability is set to NONE + // - Initiator and responder key distribution flags are set to both encryption and identity keys. + // - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it. + // - Key size is set to 16 bytes + + // Set the same static passkey as the server + // The first argument defines if the passkey is static or random. + // The second argument is the passkey (ignored when using a random passkey). + pSecurity->setPassKey(true, CLIENT_PIN); + + // Set authentication mode to match server requirements + // Enable secure connection and MITM (for password prompts) for this example + pSecurity->setAuthenticationMode(false, true, true); + + // Set IO capability to KeyboardOnly + // We need the proper IO capability for MITM authentication even + // if the passkey is static and won't be entered by the user + // See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/ + pSecurity->setCapability(ESP_IO_CAP_IN); + + // Retrieve a Scanner and set the callback we want to use to be informed when we + // have detected a new device. Specify that we want active scanning and start the + // scan to run for 5 seconds. + BLEScan *pBLEScan = BLEDevice::getScan(); + pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); + pBLEScan->setInterval(1349); + pBLEScan->setWindow(449); + pBLEScan->setActiveScan(true); + pBLEScan->start(5, false); +} + +void loop() { + // If the flag "doConnect" is true then we have scanned for and found the desired + // BLE Server with which we wish to connect. Now we connect to it. + if (doConnect == true) { + if (connectToServer()) { + Serial.println("We are now connected to the secure BLE Server."); + } else { + Serial.println("We have failed to connect to the server; there is nothing more we will do."); + } + doConnect = false; + } + + // If we are connected to a peer BLE Server, demonstrate secure communication + if (connected) { + // Write to the insecure characteristic + String insecureValue = "Client time: " + String(millis() / 1000); + if (pRemoteInsecureCharacteristic->canWrite()) { + pRemoteInsecureCharacteristic->writeValue(insecureValue.c_str(), insecureValue.length()); + Serial.println("Wrote to insecure characteristic: " + insecureValue); + } + + // Write to the secure characteristic + String secureValue = "Secure client time: " + String(millis() / 1000); + if (pRemoteSecureCharacteristic->canWrite()) { + pRemoteSecureCharacteristic->writeValue(secureValue.c_str(), secureValue.length()); + Serial.println("Wrote to secure characteristic: " + secureValue); + } + } else if (doScan) { + // Restart scanning if we're disconnected + BLEDevice::getScan()->start(0); + } + + delay(2000); // Delay 2 seconds between loops +} diff --git a/libraries/BLE/examples/Client_secure_static_passkey/ci.json b/libraries/BLE/examples/Client_secure_static_passkey/ci.json new file mode 100644 index 00000000000..e9657aad729 --- /dev/null +++ b/libraries/BLE/examples/Client_secure_static_passkey/ci.json @@ -0,0 +1,7 @@ +{ + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json +++ b/libraries/BLE/examples/EddystoneTLM_Beacon/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/EddystoneURL_Beacon/ci.json +++ b/libraries/BLE/examples/EddystoneURL_Beacon/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Notify/ci.json +++ b/libraries/BLE/examples/Notify/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Scan/ci.json +++ b/libraries/BLE/examples/Scan/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Server/ci.json +++ b/libraries/BLE/examples/Server/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Server_multiconnect/ci.json +++ b/libraries/BLE/examples/Server_multiconnect/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "requires_any": [ + "CONFIG_SOC_BLE_SUPPORTED=y", + "CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y" ] } diff --git a/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino b/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino new file mode 100644 index 00000000000..b1ab9cd5931 --- /dev/null +++ b/libraries/BLE/examples/Server_secure_authorization/Server_secure_authorization.ino @@ -0,0 +1,160 @@ +/* + Simple BLE Server Authorization Example + + This example demonstrates how to create a BLE server with authorization + requirements. It shows the essential setup for: + - Authorization with static passkey + - Secure connection + - MITM (Man-In-The-Middle) protection + + The server creates a single characteristic that requires authorization + to access. Clients must provide the correct passkey (123456) to read + or write to the characteristic. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + + Due to a bug in ESP-IDF's Bluedroid, this example will currently not work with ESP32. + + IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts + to work. Without MITM, the BLE stack assumes no user interaction is needed and will use + "Just Works" pairing method (with encryption if secure connection is enabled). + + Created by lucasssvaz. +*/ + +#include +#include +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" + +// Example passkey - change this for production use +#define AUTH_PASSKEY 123456 + +static int s_readCount = 0; +static BLECharacteristic *s_pCharacteristic; + +class MySecurityCallbacks : public BLESecurityCallbacks { + bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) { + Serial.println("Authorization request received"); + if (isRead) { + s_readCount++; + // Keep value length <= (MTU - 1) to avoid a follow-up read request + uint16_t maxLen = BLEDevice::getServer()->getPeerMTU(connHandle) - 1; + String msg = "Authorized #" + String(s_readCount); + if (msg.length() > maxLen) { + msg = msg.substring(0, maxLen); + } + s_pCharacteristic->setValue(msg); + // Grant authorization to the first 3 reads + if (s_readCount <= 3) { + Serial.println("Authorization granted"); + return true; + } else { + Serial.println("Authorization denied, read count exceeded"); + Serial.println("Please reset the read counter to continue"); + return false; + } + } + // Fallback to deny + Serial.println("Authorization denied"); + return false; + } +}; + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE Authorization Example!"); + + // Initialize the BOOT pin for resetting the read count + pinMode(BOOT_PIN, INPUT_PULLUP); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + Serial.print("Using BLE stack: "); + Serial.println(BLEDevice::getBLEStackString()); + + BLEDevice::init("BLE Auth Server"); + + // Set MTU to 517 to avoid a follow-up read request + BLEDevice::setMTU(517); + + // Configure BLE Security + BLESecurity *pSecurity = new BLESecurity(); + + // Set static passkey for authentication + pSecurity->setPassKey(true, AUTH_PASSKEY); + + // Set IO capability to DisplayOnly for MITM authentication + pSecurity->setCapability(ESP_IO_CAP_OUT); + + // Enable authorization requirements: + // - bonding: true (for persistent storage of the keys) + // - MITM: true (enables Man-In-The-Middle protection for password prompts) + // - secure connection: true (enables secure connection for encryption) + pSecurity->setAuthenticationMode(true, true, true); + + // Set the security callbacks + BLEDevice::setSecurityCallbacks(new MySecurityCallbacks()); + + // Create BLE Server + BLEServer *pServer = BLEDevice::createServer(); + pServer->advertiseOnDisconnect(true); + + // Create BLE Service + BLEService *pService = pServer->createService(SERVICE_UUID); + + // Create characteristic with read and write properties + uint32_t properties = BLECharacteristic::PROPERTY_READ; + + // For NimBLE: Add authentication properties + // These properties ensure the characteristic requires authorization + // (ignored by Bluedroid but harmless) + properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_AUTHOR; + + s_pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, properties); + + // For Bluedroid: Set access permissions that require encryption and MITM + // This ensures authorization is required (ignored by NimBLE) + s_pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_READ_AUTHORIZATION); + + // Set initial value + s_pCharacteristic->setValue("Hello! You needed authorization to read this!"); + + // Start the service + pService->start(); + + // Configure and start advertising + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // helps with iPhone connections + pAdvertising->setMinPreferred(0x12); + + BLEDevice::startAdvertising(); + + Serial.println("BLE Server is running!"); + Serial.println("Authorization is required to access the characteristic."); + Serial.printf("Use passkey: %d when prompted\n", AUTH_PASSKEY); +} + +void loop() { + // Reset the read count if the BOOT pin is pressed + if (digitalRead(BOOT_PIN) == LOW) { + s_readCount = 0; + Serial.println("Read count reset"); + } + + delay(100); +} diff --git a/libraries/BLE/examples/Server_secure_authorization/ci.json b/libraries/BLE/examples/Server_secure_authorization/ci.json new file mode 100644 index 00000000000..1e2d20da791 --- /dev/null +++ b/libraries/BLE/examples/Server_secure_authorization/ci.json @@ -0,0 +1,9 @@ +{ + "targets": { + "esp32": false + }, + "fqbn_append": "PartitionScheme=huge_app", + "requires": [ + "CONFIG_SOC_BLE_SUPPORTED=y" + ] +} diff --git a/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino b/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino new file mode 100644 index 00000000000..79b73695202 --- /dev/null +++ b/libraries/BLE/examples/Server_secure_static_passkey/Server_secure_static_passkey.ino @@ -0,0 +1,125 @@ +/* + Secure server with static passkey + + This example demonstrates how to create a secure BLE server with no + IO capability using a static passkey. + The server will accept connections from devices that have the same passkey set. + The example passkey is set to 123456. + The server will create a service and a secure and an insecure characteristic + to be used as example. + + This server is designed to be used with the Client_secure_static_passkey example. + + Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE. + Bluedroid initiates security on-connect, while NimBLE initiates security on-demand. + This means that in NimBLE you can read the insecure characteristic without entering + the passkey. This is not possible in Bluedroid. + + IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts + to work. Without MITM, the BLE stack assumes no user interaction is needed and will use + "Just Works" pairing method (with encryption if secure connection is enabled). + + Based on examples from Neil Kolban and h2zero. + Created by lucasssvaz. +*/ + +#include +#include +#include +#include +#include +#include + +// See the following for generating UUIDs: +// https://www.uuidgenerator.net/ + +#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b" +#define INSECURE_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8" +#define SECURE_CHARACTERISTIC_UUID "ff1d2614-e2d6-4c87-9154-6625d39ca7f8" + +// This is an example passkey. You should use a different or random passkey. +#define SERVER_PIN 123456 + +void setup() { + Serial.begin(115200); + Serial.println("Starting BLE work!"); + + // Clear NVS to remove any cached pairing information + // This ensures fresh authentication for testing + Serial.println("Clearing NVS pairing data..."); + nvs_flash_erase(); + nvs_flash_init(); + + Serial.print("Using BLE stack: "); + Serial.println(BLEDevice::getBLEStackString()); + + BLEDevice::init("Secure BLE Server"); + + BLESecurity *pSecurity = new BLESecurity(); + + // Set security parameters + // Default parameters: + // - IO capability is set to NONE + // - Initiator and responder key distribution flags are set to both encryption and identity keys. + // - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it. + // - Key size is set to 16 bytes + + // Set static passkey + // The first argument defines if the passkey is static or random. + // The second argument is the passkey (ignored when using a random passkey). + pSecurity->setPassKey(true, SERVER_PIN); + + // Set IO capability to DisplayOnly + // We need the proper IO capability for MITM authentication even + // if the passkey is static and won't be shown to the user + // See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/ + pSecurity->setCapability(ESP_IO_CAP_OUT); + + // Set authentication mode + // Require secure connection and MITM (for password prompts) for this example + pSecurity->setAuthenticationMode(false, true, true); + + BLEServer *pServer = BLEDevice::createServer(); + pServer->advertiseOnDisconnect(true); + + BLEService *pService = pServer->createService(SERVICE_UUID); + + uint32_t insecure_properties = BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE; + uint32_t secure_properties = insecure_properties; + + // NimBLE uses properties to secure characteristics. + // These special permission properties are not supported by Bluedroid and will be ignored. + // This can be removed if only using Bluedroid (ESP32). + // Check the BLECharacteristic.h file for more information. + secure_properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_WRITE_AUTHEN; + + BLECharacteristic *pSecureCharacteristic = pService->createCharacteristic(SECURE_CHARACTERISTIC_UUID, secure_properties); + BLECharacteristic *pInsecureCharacteristic = pService->createCharacteristic(INSECURE_CHARACTERISTIC_UUID, insecure_properties); + + // Bluedroid uses permissions to secure characteristics. + // This is the same as using the properties above. + // NimBLE does not use permissions and will ignore these calls. + // This can be removed if only using NimBLE (any SoC except ESP32). + pSecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM); + pInsecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE); + + // Set value for secure characteristic + pSecureCharacteristic->setValue("Secure Hello World!"); + + // Set value for insecure characteristic + // When using NimBLE you will be able to read this characteristic without entering the passkey. + pInsecureCharacteristic->setValue("Insecure Hello World!"); + + pService->start(); + BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->addServiceUUID(SERVICE_UUID); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue + pAdvertising->setMinPreferred(0x12); + BLEDevice::startAdvertising(); + Serial.println("Characteristic defined! Now you can read it in your phone!"); +} + +void loop() { + delay(2000); +} diff --git a/libraries/BLE/examples/Server_secure_static_passkey/ci.json b/libraries/BLE/examples/Server_secure_static_passkey/ci.json new file mode 100644 index 00000000000..e9657aad729 --- /dev/null +++ b/libraries/BLE/examples/Server_secure_static_passkey/ci.json @@ -0,0 +1,7 @@ +{ + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/UART/ci.json +++ b/libraries/BLE/examples/UART/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/Write/ci.json +++ b/libraries/BLE/examples/Write/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "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 index abe13a7ebbb..e9657aad729 100644 --- a/libraries/BLE/examples/iBeacon/ci.json +++ b/libraries/BLE/examples/iBeacon/ci.json @@ -1,6 +1,7 @@ { "fqbn_append": "PartitionScheme=huge_app", - "requires": [ - "CONFIG_SOC_BLE_SUPPORTED=y" + "requires_any": [ + "CONFIG_SOC_BLE_SUPPORTED=y", + "CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y" ] } diff --git a/libraries/BLE/src/BLE2901.cpp b/libraries/BLE/src/BLE2901.cpp index 1fa4857ac33..20535dd91df 100644 --- a/libraries/BLE/src/BLE2901.cpp +++ b/libraries/BLE/src/BLE2901.cpp @@ -16,9 +16,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -47,7 +46,7 @@ BLE2901::BLE2901() : BLEDescriptor(BLEUUID((uint16_t)BLE2901_UUID)) {} /** * @brief Set the Characteristic User Description */ -void BLE2901::setDescription(String userDesc) { +void BLE2901::setDescription(const String &userDesc) { if (userDesc.length() > ESP_GATT_MAX_ATTR_LEN) { log_e("Size %d too large, must be no bigger than %d", userDesc.length(), ESP_GATT_MAX_ATTR_LEN); return; @@ -56,4 +55,4 @@ void BLE2901::setDescription(String userDesc) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2901.h b/libraries/BLE/src/BLE2901.h index 21e7cc9398c..949cad037fd 100644 --- a/libraries/BLE/src/BLE2901.h +++ b/libraries/BLE/src/BLE2901.h @@ -19,9 +19,8 @@ #define COMPONENTS_CPP_UTILS_BLE2901_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -40,9 +39,10 @@ class BLE2901 : public BLEDescriptor { ***************************************************************************/ BLE2901(); - void setDescription(String desc); + void setDescription(const String &desc); }; // BLE2901 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2901_H_ */ diff --git a/libraries/BLE/src/BLE2902.cpp b/libraries/BLE/src/BLE2902.cpp index 3bac9281328..464524431e2 100644 --- a/libraries/BLE/src/BLE2902.cpp +++ b/libraries/BLE/src/BLE2902.cpp @@ -15,9 +15,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -126,4 +125,4 @@ void BLE2902::setNotifications(bool flag) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2902.h b/libraries/BLE/src/BLE2902.h index 5cbf911ea4a..1dc685ca476 100644 --- a/libraries/BLE/src/BLE2902.h +++ b/libraries/BLE/src/BLE2902.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLE2902_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -56,11 +55,9 @@ This class will be removed in a future version.")]] BLE2902 : public BLEDescript bool getIndications(); void setNotifications(bool flag); void setIndications(bool flag); - - private: - friend class BLECharacteristic; }; // BLE2902 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2902_H_ */ diff --git a/libraries/BLE/src/BLE2904.cpp b/libraries/BLE/src/BLE2904.cpp index 19658051120..2d3565ae0e3 100644 --- a/libraries/BLE/src/BLE2904.cpp +++ b/libraries/BLE/src/BLE2904.cpp @@ -13,10 +13,10 @@ * See also: * https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.gatt.characteristic_presentation_format.xml */ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -85,4 +85,4 @@ void BLE2904::setUnit(uint16_t unit) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLE2904.h b/libraries/BLE/src/BLE2904.h index 87e8c03c048..f0f26763985 100644 --- a/libraries/BLE/src/BLE2904.h +++ b/libraries/BLE/src/BLE2904.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLE2904_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -95,8 +94,6 @@ class BLE2904 : public BLEDescriptor { void setUnit(uint16_t unit); private: - friend class BLECharacteristic; - /*************************************************************************** * Common private properties * ***************************************************************************/ @@ -105,5 +102,6 @@ class BLE2904 : public BLEDescriptor { }; // BLE2904 #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLE2904_H_ */ diff --git a/libraries/BLE/src/BLEAddress.cpp b/libraries/BLE/src/BLEAddress.cpp index 11c64850367..e345ac9fecc 100644 --- a/libraries/BLE/src/BLEAddress.cpp +++ b/libraries/BLE/src/BLEAddress.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -59,13 +58,8 @@ BLEAddress::BLEAddress() { * @param [in] otherAddress The other address to compare against. * @return True if the addresses are equal. */ -bool BLEAddress::equals(BLEAddress otherAddress) { -#if defined(CONFIG_NIMBLE_ENABLED) - if (m_addrType != otherAddress.m_addrType) { - return false; - } -#endif - return memcmp(otherAddress.getNative(), m_address, ESP_BD_ADDR_LEN) == 0; +bool BLEAddress::equals(const BLEAddress &otherAddress) const { + return *this == otherAddress; } bool BLEAddress::operator==(const BLEAddress &otherAddress) const { @@ -116,9 +110,9 @@ uint8_t *BLEAddress::getNative() { * * @return The string representation of the address. */ -String BLEAddress::toString() { - auto size = 18; - char *res = (char *)malloc(size); +String BLEAddress::toString() const { + constexpr size_t size = 18; + char res[size]; #if defined(CONFIG_BLUEDROID_ENABLED) snprintf(res, size, "%02x:%02x:%02x:%02x:%02x:%02x", m_address[0], m_address[1], m_address[2], m_address[3], m_address[4], m_address[5]); @@ -129,7 +123,6 @@ String BLEAddress::toString() { #endif String ret(res); - free(res); return ret; } @@ -158,7 +151,7 @@ BLEAddress::BLEAddress(esp_bd_addr_t address) { * * @param [in] stringAddress The hex representation of the address. */ -BLEAddress::BLEAddress(String stringAddress) { +BLEAddress::BLEAddress(const String &stringAddress) { if (stringAddress.length() != 17) { return; } @@ -193,7 +186,7 @@ BLEAddress::BLEAddress(ble_addr_t address) { m_addrType = address.type; } -uint8_t BLEAddress::getType() { +uint8_t BLEAddress::getType() const { return m_addrType; } @@ -209,7 +202,7 @@ uint8_t BLEAddress::getType() { * @param [in] stringAddress The hex representation of the address. * @param [in] type The address type. */ -BLEAddress::BLEAddress(String stringAddress, uint8_t type) { +BLEAddress::BLEAddress(const String &stringAddress, uint8_t type) { if (stringAddress.length() != 17) { return; } @@ -227,4 +220,4 @@ BLEAddress::BLEAddress(String stringAddress, uint8_t type) { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAddress.h b/libraries/BLE/src/BLEAddress.h index 71b3bdfce5f..9b6bd422bad 100644 --- a/libraries/BLE/src/BLEAddress.h +++ b/libraries/BLE/src/BLEAddress.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADDRESS_H_ #define COMPONENTS_CPP_UTILS_BLEADDRESS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -22,7 +22,9 @@ ***************************************************************************/ #include "WString.h" +#if SOC_BLE_SUPPORTED #include +#endif #include /*************************************************************************** @@ -62,7 +64,7 @@ class BLEAddress { ***************************************************************************/ BLEAddress(); - bool equals(BLEAddress otherAddress); + bool equals(const BLEAddress &otherAddress) const; bool operator==(const BLEAddress &otherAddress) const; bool operator!=(const BLEAddress &otherAddress) const; bool operator<(const BLEAddress &otherAddress) const; @@ -70,7 +72,7 @@ class BLEAddress { bool operator>(const BLEAddress &otherAddress) const; bool operator>=(const BLEAddress &otherAddress) const; uint8_t *getNative(); - String toString(); + String toString() const; /*************************************************************************** * Bluedroid public declarations * @@ -78,7 +80,7 @@ class BLEAddress { #if defined(CONFIG_BLUEDROID_ENABLED) BLEAddress(esp_bd_addr_t address); - BLEAddress(String stringAddress); + BLEAddress(const String &stringAddress); #endif /*************************************************************************** @@ -87,9 +89,9 @@ class BLEAddress { #if defined(CONFIG_NIMBLE_ENABLED) BLEAddress(ble_addr_t address); - BLEAddress(String stringAddress, uint8_t type = BLE_ADDR_PUBLIC); + BLEAddress(const String &stringAddress, uint8_t type = BLE_ADDR_PUBLIC); BLEAddress(uint8_t address[ESP_BD_ADDR_LEN], uint8_t type = BLE_ADDR_PUBLIC); - uint8_t getType(); + uint8_t getType() const; #endif private: @@ -109,5 +111,6 @@ class BLEAddress { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADDRESS_H_ */ diff --git a/libraries/BLE/src/BLEAdvertisedDevice.cpp b/libraries/BLE/src/BLEAdvertisedDevice.cpp index 3ac1dfab5c8..8a9f7349d0f 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.cpp +++ b/libraries/BLE/src/BLEAdvertisedDevice.cpp @@ -16,8 +16,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -659,4 +659,4 @@ uint8_t BLEAdvertisedDevice::getAdvType() { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAdvertisedDevice.h b/libraries/BLE/src/BLEAdvertisedDevice.h index 2a2fc0c81aa..3f1bc6f3c4f 100644 --- a/libraries/BLE/src/BLEAdvertisedDevice.h +++ b/libraries/BLE/src/BLEAdvertisedDevice.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ #define COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -230,5 +230,6 @@ class BLEExtAdvertisingCallbacks { #endif // SOC_BLE_50_SUPPORTED && CONFIG_BLUEDROID_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISEDDEVICE_H_ */ diff --git a/libraries/BLE/src/BLEAdvertising.cpp b/libraries/BLE/src/BLEAdvertising.cpp index 4e76873dcad..41350c67487 100644 --- a/libraries/BLE/src/BLEAdvertising.cpp +++ b/libraries/BLE/src/BLEAdvertising.cpp @@ -22,9 +22,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -1425,4 +1424,4 @@ bool BLEAdvertising::stop() { #endif /* CONFIG_NIMBLE_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEAdvertising.h b/libraries/BLE/src/BLEAdvertising.h index cf68768ec91..2ea112bbdc9 100644 --- a/libraries/BLE/src/BLEAdvertising.h +++ b/libraries/BLE/src/BLEAdvertising.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ #define COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -253,5 +253,6 @@ class BLEMultiAdvertising { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEADVERTISING_H_ */ diff --git a/libraries/BLE/src/BLEBeacon.cpp b/libraries/BLE/src/BLEBeacon.cpp index 0a6c6b05258..0b10836b121 100644 --- a/libraries/BLE/src/BLEBeacon.cpp +++ b/libraries/BLE/src/BLEBeacon.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -62,7 +61,7 @@ int8_t BLEBeacon::getSignalPower() { return m_beaconData.signalPower; } -void BLEBeacon::setData(String data) { +void BLEBeacon::setData(const String &data) { if (data.length() != sizeof(m_beaconData)) { log_e("Unable to set the data ... length passed in was %d and expected %d", data.length(), sizeof(m_beaconData)); return; @@ -96,4 +95,4 @@ void BLEBeacon::setProximityUUID(BLEUUID uuid) { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEBeacon.h b/libraries/BLE/src/BLEBeacon.h index e0eaddda6e4..b3f3408a150 100644 --- a/libraries/BLE/src/BLEBeacon.h +++ b/libraries/BLE/src/BLEBeacon.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEBEACON_H_ #define COMPONENTS_CPP_UTILS_BLEBEACON_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -55,7 +55,7 @@ class BLEBeacon { uint16_t getManufacturerId(); BLEUUID getProximityUUID(); int8_t getSignalPower(); - void setData(String data); + void setData(const String &data); void setMajor(uint16_t major); void setMinor(uint16_t minor); void setManufacturerId(uint16_t manufacturerId); @@ -64,5 +64,6 @@ class BLEBeacon { }; // BLEBeacon #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEBEACON_H_ */ diff --git a/libraries/BLE/src/BLECharacteristic.cpp b/libraries/BLE/src/BLECharacteristic.cpp index 0234cd11cca..a492116b2e6 100644 --- a/libraries/BLE/src/BLECharacteristic.cpp +++ b/libraries/BLE/src/BLECharacteristic.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -124,7 +123,7 @@ void BLECharacteristic::addDescriptor(BLEDescriptor *pDescriptor) { * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ -BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID) { +BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID) const { return m_descriptorMap.getByUUID(BLEUUID(descriptorUUID)); } // getDescriptorByUUID @@ -133,7 +132,7 @@ BLEDescriptor *BLECharacteristic::getDescriptorByUUID(const char *descriptorUUID * @param [in] descriptorUUID The UUID of the descriptor that we wish to retrieve. * @return The BLE Descriptor. If no such descriptor is associated with the characteristic, nullptr is returned. */ -BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { +BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) const { return m_descriptorMap.getByUUID(descriptorUUID); } // getDescriptorByUUID @@ -141,24 +140,24 @@ BLEDescriptor *BLECharacteristic::getDescriptorByUUID(BLEUUID descriptorUUID) { * @brief Get the handle of the characteristic. * @return The handle of the characteristic. */ -uint16_t BLECharacteristic::getHandle() { +uint16_t BLECharacteristic::getHandle() const { return m_handle; } // getHandle -void BLECharacteristic::setAccessPermissions(uint8_t perm) { +void BLECharacteristic::setAccessPermissions(uint16_t perm) { #ifdef CONFIG_BLUEDROID_ENABLED m_permissions = perm; #endif } -esp_gatt_char_prop_t BLECharacteristic::getProperties() { +esp_gatt_char_prop_t BLECharacteristic::getProperties() const { return m_properties; } // getProperties /** * @brief Get the service associated with this characteristic. */ -BLEService *BLECharacteristic::getService() { +BLEService *BLECharacteristic::getService() const { return m_pService; } // getService @@ -166,7 +165,7 @@ BLEService *BLECharacteristic::getService() { * @brief Get the UUID of the characteristic. * @return The UUID of the characteristic. */ -BLEUUID BLECharacteristic::getUUID() { +BLEUUID BLECharacteristic::getUUID() const { return m_bleUUID; } // getUUID @@ -174,7 +173,7 @@ BLEUUID BLECharacteristic::getUUID() { * @brief Retrieve the current value of the characteristic. * @return A pointer to storage containing the current characteristic value. */ -String BLECharacteristic::getValue() { +String BLECharacteristic::getValue() const { return m_value.getValue(); } // getValue @@ -190,7 +189,7 @@ uint8_t *BLECharacteristic::getData() { * @brief Retrieve the current length of the data of the characteristic. * @return Amount of databytes of the characteristic. */ -size_t BLECharacteristic::getLength() { +size_t BLECharacteristic::getLength() const { return m_value.getLength(); } // getLength @@ -346,7 +345,7 @@ void BLECharacteristic::setReadProperty(bool value) { * @param [in] data The data to set for the characteristic. * @param [in] length The length of the data in bytes. */ -void BLECharacteristic::setValue(uint8_t *data, size_t length) { +void BLECharacteristic::setValue(const uint8_t *data, size_t length) { // The call to BLEUtils::buildHexData() doesn't output anything if the log level is not // "VERBOSE". As it is quite CPU intensive, it is much better to not call it if not needed. #if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE @@ -371,43 +370,28 @@ void BLECharacteristic::setValue(uint8_t *data, size_t length) { * @param [in] Set the value of the characteristic. * @return N/A. */ -void BLECharacteristic::setValue(String value) { - setValue((uint8_t *)(value.c_str()), value.length()); +void BLECharacteristic::setValue(const String &value) { + setValue(reinterpret_cast(value.c_str()), value.length()); } // setValue -void BLECharacteristic::setValue(uint16_t &data16) { - uint8_t temp[2]; - temp[0] = data16; - temp[1] = data16 >> 8; - setValue(temp, 2); +void BLECharacteristic::setValue(uint16_t data16) { + setValue(reinterpret_cast(&data16), sizeof(data16)); } // setValue -void BLECharacteristic::setValue(uint32_t &data32) { - uint8_t temp[4]; - temp[0] = data32; - temp[1] = data32 >> 8; - temp[2] = data32 >> 16; - temp[3] = data32 >> 24; - setValue(temp, 4); +void BLECharacteristic::setValue(uint32_t data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(int &data32) { - uint8_t temp[4]; - temp[0] = data32; - temp[1] = data32 >> 8; - temp[2] = data32 >> 16; - temp[3] = data32 >> 24; - setValue(temp, 4); +void BLECharacteristic::setValue(int data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(float &data32) { - float temp = data32; - setValue((uint8_t *)&temp, 4); +void BLECharacteristic::setValue(float data32) { + setValue(reinterpret_cast(&data32), sizeof(data32)); } // setValue -void BLECharacteristic::setValue(double &data64) { - double temp = data64; - setValue((uint8_t *)&temp, 8); +void BLECharacteristic::setValue(double data64) { + setValue(reinterpret_cast(&data64), sizeof(data64)); } // setValue /** @@ -440,7 +424,7 @@ void BLECharacteristic::setWriteProperty(bool value) { * @brief Return a string representation of the characteristic. * @return A string representation of the characteristic. */ -String BLECharacteristic::toString() { +String BLECharacteristic::toString() const { String res = "UUID: " + m_bleUUID.toString() + ", handle : 0x"; char hex[5]; snprintf(hex, sizeof(hex), "%04x", m_handle); @@ -467,7 +451,7 @@ String BLECharacteristic::toString() { return res; } // toString -BLECharacteristicCallbacks::~BLECharacteristicCallbacks() {} +BLECharacteristicCallbacks::~BLECharacteristicCallbacks() = default; // Common callbacks void BLECharacteristicCallbacks::onRead(BLECharacteristic *pCharacteristic) { @@ -582,6 +566,32 @@ void BLECharacteristic::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_ga // we save the new value. Next we look at the need_rsp flag which indicates whether or not we need // to send a response. If we do, then we formulate a response and send it. if (param->write.handle == m_handle) { + + // Check for authorization requirement + if (m_permissions & ESP_GATT_PERM_WRITE_AUTHORIZATION) { + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Authorization required for write operation. Checking authorization..."); + authorized = BLEDevice::m_securityCallbacks->onAuthorizationRequest(param->write.conn_id, m_handle, false); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting write authorization request"); + } + + if (!authorized) { + log_i("Write authorization rejected"); + if (param->write.need_rsp) { + esp_err_t errRc = ::esp_ble_gatts_send_response(gatts_if, param->write.conn_id, param->write.trans_id, ESP_GATT_INSUF_AUTHORIZATION, nullptr); + if (errRc != ESP_OK) { + log_e("esp_ble_gatts_send_response (authorization failed): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + return; // Exit early, don't process the write + } else { + log_i("Write authorization granted"); + } + } + if (param->write.is_prep) { m_value.addPart(param->write.value, param->write.len); m_writeEvt = true; @@ -637,6 +647,31 @@ void BLECharacteristic::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_ga { if (param->read.handle == m_handle) { + // Check for authorization requirement + if (m_permissions & ESP_GATT_PERM_READ_AUTHORIZATION) { + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Authorization required for read operation. Checking authorization..."); + authorized = BLEDevice::m_securityCallbacks->onAuthorizationRequest(param->read.conn_id, m_handle, true); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting read authorization request"); + } + + if (!authorized) { + log_i("Read authorization rejected"); + if (param->read.need_rsp) { + esp_err_t errRc = ::esp_ble_gatts_send_response(gatts_if, param->read.conn_id, param->read.trans_id, ESP_GATT_INSUF_AUTHORIZATION, nullptr); + if (errRc != ESP_OK) { + log_e("esp_ble_gatts_send_response (authorization failed): rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); + } + } + return; // Exit early, don't process the read + } else { + log_i("Read authorization granted"); + } + } + // Here's an interesting thing. The read request has the option of saying whether we need a response // or not. What would it "mean" to receive a read request and NOT send a response back? That feels like // a very strange read. @@ -887,7 +922,6 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr if (ctxt->om->om_pkthdr_len > 8) { rc = ble_gap_conn_find(conn_handle, &desc); assert(rc == 0); - pCharacteristic->m_pCallbacks->onRead(pCharacteristic); pCharacteristic->m_pCallbacks->onRead(pCharacteristic, &desc); } @@ -921,7 +955,6 @@ int BLECharacteristic::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr rc = ble_gap_conn_find(conn_handle, &desc); assert(rc == 0); pCharacteristic->setValue(buf, len); - pCharacteristic->m_pCallbacks->onWrite(pCharacteristic); pCharacteristic->m_pCallbacks->onWrite(pCharacteristic, &desc); return 0; @@ -1136,4 +1169,4 @@ void BLECharacteristicCallbacks::onSubscribe(BLECharacteristic *pCharacteristic, #endif /* CONFIG_NIMBLE_ENABLED */ #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLECharacteristic.h b/libraries/BLE/src/BLECharacteristic.h index 27df5a30c3e..b0ab8f916ea 100644 --- a/libraries/BLE/src/BLECharacteristic.h +++ b/libraries/BLE/src/BLECharacteristic.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ #define COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -62,7 +62,7 @@ #if defined(CONFIG_NIMBLE_ENABLED) typedef uint16_t esp_gatt_char_prop_t; -typedef uint8_t esp_gatt_perm_t; +typedef uint16_t esp_gatt_perm_t; #endif /*************************************************************************** @@ -85,13 +85,13 @@ class BLEDescriptorMap { void setByUUID(const char *uuid, BLEDescriptor *pDescriptor); void setByUUID(BLEUUID uuid, BLEDescriptor *pDescriptor); void setByHandle(uint16_t handle, BLEDescriptor *pDescriptor); - BLEDescriptor *getByUUID(const char *uuid); - BLEDescriptor *getByUUID(BLEUUID uuid); - BLEDescriptor *getByHandle(uint16_t handle); - String toString(); + BLEDescriptor *getByUUID(const char *uuid) const; + BLEDescriptor *getByUUID(BLEUUID uuid) const; + BLEDescriptor *getByHandle(uint16_t handle) const; + String toString() const; BLEDescriptor *getFirst(); BLEDescriptor *getNext(); - int getRegisteredDescriptorCount(); + int getRegisteredDescriptorCount() const; void removeDescriptor(BLEDescriptor *pDescriptor); /*************************************************************************** @@ -140,11 +140,17 @@ class BLECharacteristic { #if defined(CONFIG_BLUEDROID_ENABLED) static const uint32_t PROPERTY_READ = 1 << 0; + static const uint32_t PROPERTY_READ_ENC = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_READ_AUTHEN = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_READ_AUTHOR = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. static const uint32_t PROPERTY_WRITE = 1 << 1; + static const uint32_t PROPERTY_WRITE_NR = 1 << 5; + static const uint32_t PROPERTY_WRITE_ENC = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_WRITE_AUTHEN = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. + static const uint32_t PROPERTY_WRITE_AUTHOR = 0; // Not supported by Bluedroid. Use setAccessPermissions() instead. static const uint32_t PROPERTY_NOTIFY = 1 << 2; static const uint32_t PROPERTY_BROADCAST = 1 << 3; static const uint32_t PROPERTY_INDICATE = 1 << 4; - static const uint32_t PROPERTY_WRITE_NR = 1 << 5; #endif /*************************************************************************** @@ -161,8 +167,8 @@ class BLECharacteristic { static const uint32_t PROPERTY_WRITE_ENC = BLE_GATT_CHR_F_WRITE_ENC; static const uint32_t PROPERTY_WRITE_AUTHEN = BLE_GATT_CHR_F_WRITE_AUTHEN; static const uint32_t PROPERTY_WRITE_AUTHOR = BLE_GATT_CHR_F_WRITE_AUTHOR; - static const uint32_t PROPERTY_BROADCAST = BLE_GATT_CHR_F_BROADCAST; static const uint32_t PROPERTY_NOTIFY = BLE_GATT_CHR_F_NOTIFY; + static const uint32_t PROPERTY_BROADCAST = BLE_GATT_CHR_F_BROADCAST; static const uint32_t PROPERTY_INDICATE = BLE_GATT_CHR_F_INDICATE; #endif @@ -175,26 +181,26 @@ class BLECharacteristic { virtual ~BLECharacteristic(); void addDescriptor(BLEDescriptor *pDescriptor); - BLEDescriptor *getDescriptorByUUID(const char *descriptorUUID); - BLEDescriptor *getDescriptorByUUID(BLEUUID descriptorUUID); - BLEUUID getUUID(); - String getValue(); + BLEDescriptor *getDescriptorByUUID(const char *descriptorUUID) const; + BLEDescriptor *getDescriptorByUUID(BLEUUID descriptorUUID) const; + BLEUUID getUUID() const; + String getValue() const; uint8_t *getData(); - size_t getLength(); + size_t getLength() const; void indicate(); void notify(bool is_notification = true); void setCallbacks(BLECharacteristicCallbacks *pCallbacks); - void setValue(uint8_t *data, size_t size); - void setValue(String value); - void setValue(uint16_t &data16); - void setValue(uint32_t &data32); - void setValue(int &data32); - void setValue(float &data32); - void setValue(double &data64); - String toString(); - uint16_t getHandle(); - void setAccessPermissions(uint8_t perm); - esp_gatt_char_prop_t getProperties(); + void setValue(const uint8_t *data, size_t size); + void setValue(const String &value); + void setValue(uint16_t data16); + void setValue(uint32_t data32); + void setValue(int data32); + void setValue(float data32); + void setValue(double data64); + String toString() const; + uint16_t getHandle() const; + void setAccessPermissions(uint16_t perm); + esp_gatt_char_prop_t getProperties() const; void setReadProperty(bool value); void setWriteProperty(bool value); void setNotifyProperty(bool value); @@ -247,7 +253,7 @@ class BLECharacteristic { ***************************************************************************/ void executeCreate(BLEService *pService); - BLEService *getService(); + BLEService *getService() const; void setHandle(uint16_t handle); /*************************************************************************** @@ -324,5 +330,6 @@ class BLECharacteristicCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLECHARACTERISTIC_H_ */ diff --git a/libraries/BLE/src/BLECharacteristicMap.cpp b/libraries/BLE/src/BLECharacteristicMap.cpp index ec3588bcde4..f82f5be47bf 100644 --- a/libraries/BLE/src/BLECharacteristicMap.cpp +++ b/libraries/BLE/src/BLECharacteristicMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -36,7 +35,7 @@ * @param [in] handle The handle to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) { +BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) const { return m_handleMap.at(handle); } // getByHandle @@ -45,7 +44,7 @@ BLECharacteristic *BLECharacteristicMap::getByHandle(uint16_t handle) { * @param [in] UUID The UUID to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) { +BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) const { return getByUUID(BLEUUID(uuid)); } @@ -54,7 +53,7 @@ BLECharacteristic *BLECharacteristicMap::getByUUID(const char *uuid) { * @param [in] UUID The UUID to look up the characteristic. * @return The characteristic. */ -BLECharacteristic *BLECharacteristicMap::getByUUID(BLEUUID uuid) { +BLECharacteristic *BLECharacteristicMap::getByUUID(BLEUUID uuid) const { for (auto &myPair : m_uuidMap) { if (myPair.first->getUUID().equals(uuid)) { return myPair.first; @@ -95,7 +94,7 @@ BLECharacteristic *BLECharacteristicMap::getNext() { * @brief Get the number of registered characteristics. * @return The number of registered characteristics. */ -int BLECharacteristicMap::getRegisteredCharacteristicCount() { +int BLECharacteristicMap::getRegisteredCharacteristicCount() const { return m_uuidMap.size(); } // getRegisteredCharacteristicCount @@ -133,7 +132,7 @@ void BLECharacteristicMap::setByUUID(BLECharacteristic *pCharacteristic, BLEUUID * @brief Return a string representation of the characteristic map. * @return A string representation of the characteristic map. */ -String BLECharacteristicMap::toString() { +String BLECharacteristicMap::toString() const { String res; int count = 0; char hex[5]; @@ -183,4 +182,4 @@ void BLECharacteristicMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEClient.cpp b/libraries/BLE/src/BLEClient.cpp index c101993d002..808614ba5bf 100644 --- a/libraries/BLE/src/BLEClient.cpp +++ b/libraries/BLE/src/BLEClient.cpp @@ -10,16 +10,18 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#include "Arduino.h" +#if SOC_BLE_SUPPORTED #include +#endif #include "BLEClient.h" #include "BLEUtils.h" #include "BLEService.h" @@ -29,6 +31,7 @@ #include #include "BLEDevice.h" #include "esp32-hal-log.h" +#include "BLESecurity.h" /*************************************************************************** * Bluedroid includes * @@ -534,6 +537,8 @@ void BLEClient::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t m_semaphoreRssiCmplEvt.give(); m_semaphoreSearchCmplEvt.give(1); BLEDevice::removePeerDevice(m_appId, true); + // Reset security state on disconnect + BLESecurity::resetSecurity(); if (m_wasConnected && m_pClientCallbacks != nullptr) { m_pClientCallbacks->onDisconnect(this); } @@ -598,11 +603,11 @@ void BLEClient::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t if (errRc != ESP_OK) { log_e("esp_ble_gattc_send_mtu_req: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); } -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(evtParam->connect.remote_bda, BLEDevice::m_securityLevel); + // Set encryption on connect for BlueDroid when security is enabled + // This ensures security is established before any secure operations + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(evtParam->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTC_CONNECT_EVT @@ -979,6 +984,8 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { BLEDevice::removePeerDevice(client->m_appId, true); client->m_isConnected = false; + // Reset security state on disconnect + BLESecurity::resetSecurity(); if (client->m_pClientCallbacks != nullptr) { client->m_pClientCallbacks->onDisconnect(client); } @@ -1006,6 +1013,10 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { break; } + if (BLESecurity::m_securityEnabled) { + BLESecurity::startSecurity(client->m_conn_id); + } + // In the case of a multiconnecting device we ignore this device when // scanning since we are already connected to it // BLEDevice::addIgnored(client->m_peerAddress); @@ -1136,8 +1147,6 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { ble_store_util_delete_peer(&desc.peer_id_addr); } else if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); - } else { - client->m_pClientCallbacks->onAuthenticationComplete(&desc); } } @@ -1164,27 +1173,52 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { } if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + // Display the passkey on this device + log_d("BLE_SM_IOACT_DISP"); + pkey.action = event->passkey.params.action; - pkey.passkey = BLESecurity::m_passkey; // This is the passkey to be entered on peer + pkey.passkey = BLESecurity::getPassKey(); // This is the passkey to be entered on peer + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); + } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); + } + + if (BLEDevice::m_securityCallbacks != nullptr) { + BLEDevice::m_securityCallbacks->onPassKeyNotify(pkey.passkey); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + // Check if the passkey on the peer device is correct + log_d("BLE_SM_IOACT_NUMCMP"); + log_d("Passkey on device's display: %d", event->passkey.params.numcmp); pkey.action = event->passkey.params.action; - // Compatibility only - Do not use, should be removed the in future + if (BLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = BLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); - //////////////////////////////////////////////////// } else { - pkey.numcmp_accept = client->m_pClientCallbacks->onConfirmPIN(event->passkey.params.numcmp); + log_e("onConfirmPIN not implemented. Rejecting connection"); + pkey.numcmp_accept = 0; } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); - //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + // Out of band pairing + // TODO: Handle out of band pairing + log_w("BLE_SM_IOACT_OOB: Not implemented"); + static uint8_t tem_oob[16] = {0}; pkey.action = event->passkey.params.action; for (int i = 0; i < 16; i++) { @@ -1192,24 +1226,35 @@ int BLEClient::handleGAPEvent(struct ble_gap_event *event, void *arg) { } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); - //////// } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { - log_d("Enter the passkey"); + // Input passkey from peer device + log_d("BLE_SM_IOACT_INPUT"); + pkey.action = event->passkey.params.action; + pkey.passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } + } - // Compatibility only - Do not use, should be removed the in future - if (BLEDevice::m_securityCallbacks != nullptr) { - pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); - ///////////////////////////////////////////// + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); } else { - pkey.passkey = client->m_pClientCallbacks->onPassKeyRequest(); + log_i("Passkey: %d", pkey.passkey); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { - log_d("No passkey action required"); + log_d("BLE_SM_IOACT_NONE"); + log_i("No passkey action required"); } return 0; @@ -1255,21 +1300,7 @@ bool BLEClientCallbacks::onConnParamsUpdateRequest(BLEClient *pClient, const ble return true; } -uint32_t BLEClientCallbacks::onPassKeyRequest() { - log_d("onPassKeyRequest: default: 123456"); - return 123456; -} - -void BLEClientCallbacks::onAuthenticationComplete(ble_gap_conn_desc *desc) { - log_d("onAuthenticationComplete: default"); -} - -bool BLEClientCallbacks::onConfirmPIN(uint32_t pin) { - log_d("onConfirmPIN: default: true"); - return true; -} - #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEClient.h b/libraries/BLE/src/BLEClient.h index 97ef72f4917..cbe0bde7e75 100644 --- a/libraries/BLE/src/BLEClient.h +++ b/libraries/BLE/src/BLEClient.h @@ -13,9 +13,8 @@ #define MAIN_BLECLIENT_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -209,12 +208,10 @@ class BLEClientCallbacks { #if defined(CONFIG_NIMBLE_ENABLED) virtual bool onConnParamsUpdateRequest(BLEClient *pClient, const ble_gap_upd_params *params); - virtual uint32_t onPassKeyRequest(); - virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); - virtual bool onConfirmPIN(uint32_t pin); #endif }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* MAIN_BLECLIENT_H_ */ diff --git a/libraries/BLE/src/BLEDescriptor.cpp b/libraries/BLE/src/BLEDescriptor.cpp index 51c77bb9b49..fe5212b6f76 100644 --- a/libraries/BLE/src/BLEDescriptor.cpp +++ b/libraries/BLE/src/BLEDescriptor.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -109,7 +108,7 @@ void BLEDescriptor::executeCreate(BLECharacteristic *pCharacteristic) { * @brief Get the BLE handle for this descriptor. * @return The handle for this descriptor. */ -uint16_t BLEDescriptor::getHandle() { +uint16_t BLEDescriptor::getHandle() const { return m_handle; } // getHandle @@ -117,14 +116,14 @@ uint16_t BLEDescriptor::getHandle() { * @brief Get the length of the value of this descriptor. * @return The length (in bytes) of the value of this descriptor. */ -size_t BLEDescriptor::getLength() { +size_t BLEDescriptor::getLength() const { return m_value.attr_len; } // getLength /** * @brief Get the UUID of the descriptor. */ -BLEUUID BLEDescriptor::getUUID() { +BLEUUID BLEDescriptor::getUUID() const { return m_bleUUID; } // getUUID @@ -132,7 +131,7 @@ BLEUUID BLEDescriptor::getUUID() { * @brief Get the value of this descriptor. * @return A pointer to the value of this descriptor. */ -uint8_t *BLEDescriptor::getValue() { +uint8_t *BLEDescriptor::getValue() const { return m_value.attr_value; } // getValue @@ -140,7 +139,7 @@ uint8_t *BLEDescriptor::getValue() { * @brief Get the characteristic this descriptor belongs to. * @return A pointer to the characteristic this descriptor belongs to. */ -BLECharacteristic *BLEDescriptor::getCharacteristic() { +BLECharacteristic *BLEDescriptor::getCharacteristic() const { return m_pCharacteristic; } // getCharacteristic @@ -181,7 +180,7 @@ void BLEDescriptor::setHandle(uint16_t handle) { * @param [in] data The data to set for the descriptor. * @param [in] length The length of the data in bytes. */ -void BLEDescriptor::setValue(uint8_t *data, size_t length) { +void BLEDescriptor::setValue(const uint8_t *data, size_t length) { if (length > m_value.attr_max_len) { log_e("Size %d too large, must be no bigger than %d", length, m_value.attr_max_len); return; @@ -203,11 +202,11 @@ void BLEDescriptor::setValue(uint8_t *data, size_t length) { * @brief Set the value of the descriptor. * @param [in] value The value of the descriptor in string form. */ -void BLEDescriptor::setValue(String value) { - setValue((uint8_t *)value.c_str(), value.length()); +void BLEDescriptor::setValue(const String &value) { + setValue(reinterpret_cast(value.c_str()), value.length()); } // setValue -void BLEDescriptor::setAccessPermissions(uint8_t perm) { +void BLEDescriptor::setAccessPermissions(uint16_t perm) { m_permissions = perm; } @@ -215,14 +214,14 @@ void BLEDescriptor::setAccessPermissions(uint8_t perm) { * @brief Return a string representation of the descriptor. * @return A string representation of the descriptor. */ -String BLEDescriptor::toString() { +String BLEDescriptor::toString() const { char hex[5]; snprintf(hex, sizeof(hex), "%04x", m_handle); String res = "UUID: " + m_bleUUID.toString() + ", handle: 0x" + hex; return res; } // toString -BLEDescriptorCallbacks::~BLEDescriptorCallbacks() {} +BLEDescriptorCallbacks::~BLEDescriptorCallbacks() = default; /** * @brief Callback function to support a read request. @@ -406,4 +405,4 @@ int BLEDescriptor::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr_han #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDescriptor.h b/libraries/BLE/src/BLEDescriptor.h index d0d34b24388..be107796cb3 100644 --- a/libraries/BLE/src/BLEDescriptor.h +++ b/libraries/BLE/src/BLEDescriptor.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ #define COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -42,6 +42,9 @@ #include #include "BLEConnInfo.h" +// Bluedroid compatibility +// NimBLE does not support signed reads and writes + #define ESP_GATT_PERM_READ BLE_ATT_F_READ #define ESP_GATT_PERM_WRITE BLE_ATT_F_WRITE #define ESP_GATT_PERM_READ_ENCRYPTED BLE_ATT_F_READ_ENC @@ -86,18 +89,18 @@ class BLEDescriptor { BLEDescriptor(BLEUUID uuid, uint16_t max_len = 100); virtual ~BLEDescriptor(); - uint16_t getHandle(); // Get the handle of the descriptor. - size_t getLength(); // Get the length of the value of the descriptor. - BLEUUID getUUID(); // Get the UUID of the descriptor. - uint8_t *getValue(); // Get a pointer to the value of the descriptor. - BLECharacteristic *getCharacteristic(); // Get the characteristic that this descriptor belongs to. + uint16_t getHandle() const; // Get the handle of the descriptor. + size_t getLength() const; // Get the length of the value of the descriptor. + BLEUUID getUUID() const; // Get the UUID of the descriptor. + uint8_t *getValue() const; // Get a pointer to the value of the descriptor. + BLECharacteristic *getCharacteristic() const; // Get the characteristic that this descriptor belongs to. - void setAccessPermissions(uint8_t perm); // Set the permissions of the descriptor. + void setAccessPermissions(uint16_t perm); // Set the permissions of the descriptor. void setCallbacks(BLEDescriptorCallbacks *pCallbacks); // Set callbacks to be invoked for the descriptor. - void setValue(uint8_t *data, size_t size); // Set the value of the descriptor as a pointer to data. - void setValue(String value); // Set the value of the descriptor as a data buffer. + void setValue(const uint8_t *data, size_t size); // Set the value of the descriptor as a pointer to data. + void setValue(const String &value); // Set the value of the descriptor as a data buffer. - String toString(); // Convert the descriptor to a string representation. + String toString() const; // Convert the descriptor to a string representation. /*************************************************************************** * Bluedroid public declarations * @@ -140,7 +143,7 @@ class BLEDescriptor { #if defined(CONFIG_BLUEDROID_ENABLED) FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); - uint8_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; + uint16_t m_permissions = ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE; #endif /*************************************************************************** @@ -179,5 +182,6 @@ class BLEDescriptorCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEDESCRIPTOR_H_ */ diff --git a/libraries/BLE/src/BLEDescriptorMap.cpp b/libraries/BLE/src/BLEDescriptorMap.cpp index e0c8b3a7cc3..39a54dc27bf 100644 --- a/libraries/BLE/src/BLEDescriptorMap.cpp +++ b/libraries/BLE/src/BLEDescriptorMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -52,7 +51,7 @@ * @param [in] UUID The UUID to look up the descriptor. * @return The descriptor. If not present, then nullptr is returned. */ -BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) { +BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) const { return getByUUID(BLEUUID(uuid)); } @@ -61,7 +60,7 @@ BLEDescriptor *BLEDescriptorMap::getByUUID(const char *uuid) { * @param [in] UUID The UUID to look up the descriptor. * @return The descriptor. If not present, then nullptr is returned. */ -BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) { +BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) const { for (auto &myPair : m_uuidMap) { if (myPair.first->getUUID().equals(uuid)) { return myPair.first; @@ -76,7 +75,7 @@ BLEDescriptor *BLEDescriptorMap::getByUUID(BLEUUID uuid) { * @param [in] handle The handle to look up the descriptor. * @return The descriptor. */ -BLEDescriptor *BLEDescriptorMap::getByHandle(uint16_t handle) { +BLEDescriptor *BLEDescriptorMap::getByHandle(uint16_t handle) const { return m_handleMap.at(handle); } // getByHandle @@ -114,7 +113,7 @@ void BLEDescriptorMap::setByHandle(uint16_t handle, BLEDescriptor *pDescriptor) * @brief Get the number of registered descriptors. * @return The number of registered descriptors. */ -int BLEDescriptorMap::getRegisteredDescriptorCount() { +int BLEDescriptorMap::getRegisteredDescriptorCount() const { return m_uuidMap.size(); } @@ -132,7 +131,7 @@ void BLEDescriptorMap::removeDescriptor(BLEDescriptor *pDescriptor) { * @brief Return a string representation of the descriptor map. * @return A string representation of the descriptor map. */ -String BLEDescriptorMap::toString() { +String BLEDescriptorMap::toString() const { String res; char hex[5]; int count = 0; @@ -213,4 +212,4 @@ void BLEDescriptorMap::handleGATTServerEvent(uint16_t conn_handle, uint16_t attr #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDevice.cpp b/libraries/BLE/src/BLEDevice.cpp index 014806cc32b..d56786e8e18 100644 --- a/libraries/BLE/src/BLEDevice.cpp +++ b/libraries/BLE/src/BLEDevice.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -24,7 +23,10 @@ #include #include #include + +#if SOC_BLE_SUPPORTED #include +#endif #include #include @@ -35,6 +37,7 @@ #include "BLEClient.h" #include "BLEUtils.h" #include "GeneralUtils.h" +#include "BLESecurity.h" #if defined(ARDUINO_ARCH_ESP32) #include "esp32-hal-bt.h" @@ -74,6 +77,10 @@ #include "services/gatt/ble_svc_gatt.h" #endif +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#include "esp32-hal-hosted.h" +#endif + /*************************************************************************** * Common properties * ***************************************************************************/ @@ -97,7 +104,6 @@ gap_event_handler BLEDevice::m_customGapHandler = nullptr; ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) -esp_ble_sec_act_t BLEDevice::m_securityLevel = (esp_ble_sec_act_t)0; gattc_event_handler BLEDevice::m_customGattcHandler = nullptr; gatts_event_handler BLEDevice::m_customGattsHandler = nullptr; #endif @@ -236,6 +242,8 @@ String BLEDevice::getValue(BLEAddress bdAddress, BLEUUID serviceUUID, BLEUUID ch */ void BLEDevice::init(String deviceName) { if (!initialized) { + log_i("Initializing BLE stack: %s", getBLEStackString().c_str()); + esp_err_t errRc = ESP_OK; #if defined(CONFIG_BLUEDROID_ENABLED) #if defined(ARDUINO_ARCH_ESP32) @@ -339,6 +347,16 @@ void BLEDevice::init(String deviceName) { #endif // CONFIG_BLE_SMP_ENABLE #endif // CONFIG_BLUEDROID_ENABLED +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + // Initialize esp-hosted transport for BLE HCI when explicitly enabled + if (!hostedInitBLE()) { + log_e("Failed to initialize ESP-Hosted for BLE"); + return; + } + + // Hosted HCI driver will be initialized automatically by NimBLE transport layer +#endif + #if defined(CONFIG_NIMBLE_ENABLED) errRc = nimble_port_init(); if (errRc != ESP_OK) { @@ -410,10 +428,14 @@ void BLEDevice::init(String deviceName) { */ void BLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powerType) { log_v(">> setPower: %d (type: %d)", powerLevel, powerType); +#if defined(SOC_BLE_SUPPORTED) esp_err_t errRc = ::esp_ble_tx_power_set(powerType, powerLevel); if (errRc != ESP_OK) { log_e("esp_ble_tx_power_set: rc=%d %s", errRc, GeneralUtils::errorToString(errRc)); }; +#else + log_w("setPower not supported with hosted HCI - power controlled by co-processor"); +#endif log_v("<< setPower"); } // setPower @@ -436,6 +458,7 @@ void BLEDevice::setPower(esp_power_level_t powerLevel, esp_ble_power_type_t powe */ int BLEDevice::getPower(esp_ble_power_type_t powerType) { +#if SOC_BLE_SUPPORTED switch (esp_ble_tx_power_get(powerType)) { case ESP_PWR_LVL_N12: return -12; case ESP_PWR_LVL_N9: return -9; @@ -447,6 +470,10 @@ int BLEDevice::getPower(esp_ble_power_type_t powerType) { case ESP_PWR_LVL_P9: return 9; default: return -128; } +#else + log_w("getPower not supported with hosted HCI - power controlled by co-processor"); + return 0; // Return default power level +#endif } // getPower /** @@ -681,13 +708,21 @@ void BLEDevice::deinit(bool release_memory) { nimble_port_deinit(); #endif +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + hostedDeinitBLE(); +#endif + +#if CONFIG_BT_CONTROLLER_ENABLED esp_bt_controller_disable(); esp_bt_controller_deinit(); +#endif #ifdef ARDUINO_ARCH_ESP32 if (release_memory) { // 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; @@ -709,6 +744,33 @@ void BLEDevice::setCustomGapHandler(gap_event_handler handler) { #endif } +BLEStack BLEDevice::getBLEStack() { +#if defined(CONFIG_BLUEDROID_ENABLED) + return BLEStack::BLUEDROID; +#elif defined(CONFIG_NIMBLE_ENABLED) + return BLEStack::NIMBLE; +#else + return BLEStack::UNKNOWN; +#endif +} + +String BLEDevice::getBLEStackString() { + switch (getBLEStack()) { + case BLEStack::BLUEDROID: return "Bluedroid"; + case BLEStack::NIMBLE: return "NimBLE"; + case BLEStack::UNKNOWN: + default: return "Unknown"; + } +} + +bool BLEDevice::isHostedBLE() { +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + return true; +#else + return false; +#endif +} + /*************************************************************************** * Bluedroid functions * ***************************************************************************/ @@ -730,11 +792,9 @@ void BLEDevice::gattServerEventHandler(esp_gatts_cb_event_t event, esp_gatt_if_t switch (event) { case ESP_GATTS_CONNECT_EVT: { -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(param->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT @@ -771,11 +831,11 @@ void BLEDevice::gattClientEventHandler(esp_gattc_cb_event_t event, esp_gatt_if_t switch (event) { case ESP_GATTC_CONNECT_EVT: { -#ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityLevel) { - esp_ble_set_encryption(param->connect.remote_bda, BLEDevice::m_securityLevel); + // Set encryption on connect for BlueDroid when security is enabled + // This ensures security is established before any secure operations + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(param->connect.remote_bda); } -#endif // CONFIG_BLE_SMP_ENABLE break; } // ESP_GATTS_CONNECT_EVT @@ -807,26 +867,49 @@ void BLEDevice::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_par case ESP_GAP_BLE_OOB_REQ_EVT: /* OOB request event */ log_i("ESP_GAP_BLE_OOB_REQ_EVT"); break; case ESP_GAP_BLE_LOCAL_IR_EVT: /* BLE local IR event */ log_i("ESP_GAP_BLE_LOCAL_IR_EVT"); break; case ESP_GAP_BLE_LOCAL_ER_EVT: /* BLE local ER event */ log_i("ESP_GAP_BLE_LOCAL_ER_EVT"); break; - case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ log_i("ESP_GAP_BLE_NC_REQ_EVT"); + case ESP_GAP_BLE_NC_REQ_EVT: /* NUMERIC CONFIRMATION */ + { + log_i("ESP_GAP_BLE_NC_REQ_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onConfirmPIN(param->ble_security.key_notif.passkey)); + } else { + log_e("onConfirmPIN not implemented. Rejecting connection"); + esp_ble_confirm_reply(param->ble_security.ble_req.bd_addr, false); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; case ESP_GAP_BLE_PASSKEY_REQ_EVT: /* passkey request event */ + { log_i("ESP_GAP_BLE_PASSKEY_REQ_EVT: "); // esp_log_buffer_hex(m_remote_bda, sizeof(m_remote_bda)); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - if (BLEDevice::m_securityCallbacks != nullptr) { - esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, BLEDevice::m_securityCallbacks->onPassKeyRequest()); + uint32_t passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } + } + + if (BLESecurity::m_staticPasskey && passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", passkey); } + + esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, passkey); #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; /* * TODO should we add white/black list comparison? */ case ESP_GAP_BLE_SEC_REQ_EVT: + { /* send the positive(true) security response to the peer device to accept the security request. If not accept the security request, should sent the security response with negative(false) accept value*/ log_i("ESP_GAP_BLE_SEC_REQ_EVT"); @@ -834,37 +917,54 @@ void BLEDevice::gapEventHandler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_par if (BLEDevice::m_securityCallbacks != nullptr) { esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, BLEDevice::m_securityCallbacks->onSecurityRequest()); } else { + log_w("onSecurityRequest not implemented. Accepting security request"); esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; /* * */ case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: //the app will receive this evt when the IO has Output capability and the peer device IO has Input capability. + { //display the passkey number to the user to input it in the peer device within 30 seconds log_i("ESP_GAP_BLE_PASSKEY_NOTIF_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig - log_i("passKey = %d", param->ble_security.key_notif.passkey); + uint32_t passkey = param->ble_security.key_notif.passkey; + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); + } + + if (BLESecurity::m_staticPasskey && passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", passkey); + } + if (BLEDevice::m_securityCallbacks != nullptr) { - BLEDevice::m_securityCallbacks->onPassKeyNotify(param->ble_security.key_notif.passkey); + BLEDevice::m_securityCallbacks->onPassKeyNotify(passkey); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; case ESP_GAP_BLE_KEY_EVT: + { //shows the ble key type info share with peer device to the user. log_d("ESP_GAP_BLE_KEY_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig log_i("key type = %s", BLESecurity::esp_key_type_to_str(param->ble_security.ble_key.key_type)); #endif // CONFIG_BLE_SMP_ENABLE - break; - case ESP_GAP_BLE_AUTH_CMPL_EVT: log_i("ESP_GAP_BLE_AUTH_CMPL_EVT"); + } break; + case ESP_GAP_BLE_AUTH_CMPL_EVT: + { + log_i("ESP_GAP_BLE_AUTH_CMPL_EVT"); #ifdef CONFIG_BLE_SMP_ENABLE // Check that BLE SMP (security) is configured in make menuconfig if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(param->ble_security.auth_cmpl); } #endif // CONFIG_BLE_SMP_ENABLE - break; + } break; default: { break; @@ -896,14 +996,6 @@ void BLEDevice::setCustomGattsHandler(gatts_event_handler handler) { m_customGattsHandler = handler; } -/* - * @brief Set encryption level that will be negotiated with peer device durng connection - * @param [in] level Requested encryption level - */ -void BLEDevice::setEncryptionLevel(esp_ble_sec_act_t level) { - BLEDevice::m_securityLevel = level; -} - #endif /*************************************************************************** @@ -1062,4 +1154,4 @@ int BLEDeviceCallbacks::onStoreStatus(struct ble_store_status_event *event, void #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEDevice.h b/libraries/BLE/src/BLEDevice.h index 66cfa2b371a..ba29c735afd 100644 --- a/libraries/BLE/src/BLEDevice.h +++ b/libraries/BLE/src/BLEDevice.h @@ -11,10 +11,10 @@ #ifndef MAIN_BLEDevice_H_ #define MAIN_BLEDevice_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -22,8 +22,19 @@ ***************************************************************************/ #include -#include + +#if defined(SOC_BLE_SUPPORTED) #include +#else +// For ESP32-P4 and other chips without native BLE support +// Define minimal types needed for interface compatibility +typedef int esp_power_level_t; +typedef int esp_ble_power_type_t; +#define ESP_BLE_PWR_TYPE_DEFAULT 0 +#define ESP_PWR_LVL_N12 0 +#endif + +#include "WString.h" #include "BLEServer.h" #include "BLEClient.h" #include "BLEUtils.h" @@ -32,6 +43,18 @@ #include "BLESecurity.h" #include "BLEAddress.h" #include "BLEUtils.h" +#include "BLEUUID.h" +#include "BLEAdvertisedDevice.h" + +/*************************************************************************** + * Common definitions * + ***************************************************************************/ + +enum class BLEStack { + BLUEDROID, + NIMBLE, + UNKNOWN +}; /*************************************************************************** * Bluedroid includes * @@ -51,6 +74,9 @@ #include #define ESP_GATT_IF_NONE BLE_HS_CONN_HANDLE_NONE +// Hosted HCI transport implementation is provided in BLEHostedHCI.cpp +// and is automatically linked when building for ESP32-P4 + // NimBLE configuration compatibility macros #if defined(CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR) && !defined(CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE) #define CONFIG_BTDM_SCAN_DUPL_TYPE_DEVICE CONFIG_SCAN_DUPLICATE_BY_DEVICE_ADDR @@ -141,7 +167,6 @@ class BLEDevice { ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - static esp_ble_sec_act_t m_securityLevel; static gattc_event_handler m_customGattcHandler; static gatts_event_handler m_customGattsHandler; #endif @@ -179,13 +204,15 @@ class BLEDevice { static BLEClient *getClientByGattIf(uint16_t conn_id); static void setCustomGapHandler(gap_event_handler handler); static void deinit(bool release_memory = false); + static BLEStack getBLEStack(); + static String getBLEStackString(); + static bool isHostedBLE(); /*************************************************************************** * Bluedroid public declarations * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - static void setEncryptionLevel(esp_ble_sec_act_t level); static void setCustomGattcHandler(gattc_event_handler handler); static void setCustomGattsHandler(gatts_event_handler handler); #endif @@ -262,5 +289,6 @@ class BLEDeviceCallbacks { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* MAIN_BLEDevice_H_ */ diff --git a/libraries/BLE/src/BLEEddystoneTLM.cpp b/libraries/BLE/src/BLEEddystoneTLM.cpp index ca0fb41b1a2..8b642c8136f 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.cpp +++ b/libraries/BLE/src/BLEEddystoneTLM.cpp @@ -14,10 +14,10 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) + #include #include #include "esp32-hal-log.h" @@ -181,4 +181,4 @@ void BLEEddystoneTLM::setTime(uint32_t tmil) { } // setTime #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEEddystoneTLM.h b/libraries/BLE/src/BLEEddystoneTLM.h index 17915a670da..2aa30c2d21b 100644 --- a/libraries/BLE/src/BLEEddystoneTLM.h +++ b/libraries/BLE/src/BLEEddystoneTLM.h @@ -11,10 +11,10 @@ #ifndef _BLEEddystoneTLM_H_ #define _BLEEddystoneTLM_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLEUUID.h" @@ -65,5 +65,6 @@ class BLEEddystoneTLM { }; // BLEEddystoneTLM #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEEddystoneTLM_H_ */ diff --git a/libraries/BLE/src/BLEEddystoneURL.cpp b/libraries/BLE/src/BLEEddystoneURL.cpp index 495671ca49b..00ca18b71d3 100644 --- a/libraries/BLE/src/BLEEddystoneURL.cpp +++ b/libraries/BLE/src/BLEEddystoneURL.cpp @@ -13,10 +13,10 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) + #include #include "esp32-hal-log.h" #include "BLEEddystoneURL.h" @@ -174,34 +174,22 @@ void BLEEddystoneURL::setUUID(BLEUUID l_uuid) { } // setUUID void BLEEddystoneURL::setPower(esp_power_level_t advertisedTxPower) { - int tx_power; + int tx_power = 0; +#if SOC_BLE_SUPPORTED switch (advertisedTxPower) { - case ESP_PWR_LVL_N12: // 12dbm - tx_power = -12; - break; - case ESP_PWR_LVL_N9: // -9dbm - tx_power = -9; - break; - case ESP_PWR_LVL_N6: // -6dbm - tx_power = -6; - break; - case ESP_PWR_LVL_N3: // -3dbm - tx_power = -3; - break; - case ESP_PWR_LVL_N0: // 0dbm - tx_power = 0; - break; - case ESP_PWR_LVL_P3: // +3dbm - tx_power = +3; - break; - case ESP_PWR_LVL_P6: // +6dbm - tx_power = +6; - break; - case ESP_PWR_LVL_P9: // +9dbm - tx_power = +9; - break; - default: tx_power = 0; + case ESP_PWR_LVL_N12: tx_power = -12; break; + case ESP_PWR_LVL_N9: tx_power = -9; break; + case ESP_PWR_LVL_N6: tx_power = -6; break; + case ESP_PWR_LVL_N3: tx_power = -3; break; + case ESP_PWR_LVL_N0: tx_power = 0; break; + case ESP_PWR_LVL_P3: tx_power = +3; break; + case ESP_PWR_LVL_P6: tx_power = +6; break; + case ESP_PWR_LVL_P9: tx_power = +9; break; + default: tx_power = 0; break; } +#else + log_w("setPower not supported with hosted HCI - power controlled by co-processor"); +#endif m_eddystoneData.advertisedTxPower = int8_t((tx_power - -100) / 2); } // setPower @@ -300,4 +288,4 @@ void BLEEddystoneURL::_initHeadder() { } #endif -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEEddystoneURL.h b/libraries/BLE/src/BLEEddystoneURL.h index 9ed89a23694..f22035093ea 100644 --- a/libraries/BLE/src/BLEEddystoneURL.h +++ b/libraries/BLE/src/BLEEddystoneURL.h @@ -14,15 +14,17 @@ #ifndef _BLEEddystoneURL_H_ #define _BLEEddystoneURL_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLEUUID.h" #include -#include "esp_bt.h" +#if SOC_BLE_SUPPORTED +#include +#endif #define EDDYSTONE_URL_FRAME_TYPE 0x10 @@ -65,5 +67,6 @@ class BLEEddystoneURL { }; // BLEEddystoneURL #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEEddystoneURL_H_ */ diff --git a/libraries/BLE/src/BLEExceptions.cpp b/libraries/BLE/src/BLEExceptions.cpp index b88ea337493..f566f7851b0 100644 --- a/libraries/BLE/src/BLEExceptions.cpp +++ b/libraries/BLE/src/BLEExceptions.cpp @@ -6,8 +6,9 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) //#include "BLEExceptions.h" -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEExceptions.h b/libraries/BLE/src/BLEExceptions.h index 15b4ef93d84..91cb184cb27 100644 --- a/libraries/BLE/src/BLEExceptions.h +++ b/libraries/BLE/src/BLEExceptions.h @@ -7,10 +7,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ #define COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if CONFIG_CXX_EXCEPTIONS != 1 #error "C++ exception handling must be enabled within make menuconfig. See Compiler Options > Enable C++ Exceptions." @@ -30,5 +30,6 @@ class BLEUuidNotFoundException : public std::exception { } }; -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEEXCEPTIONS_H_ */ diff --git a/libraries/BLE/src/BLEHIDDevice.cpp b/libraries/BLE/src/BLEHIDDevice.cpp index a255879e2d8..20f9e800bb9 100644 --- a/libraries/BLE/src/BLEHIDDevice.cpp +++ b/libraries/BLE/src/BLEHIDDevice.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -282,4 +281,4 @@ BLEService *BLEHIDDevice::batteryService() { } #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEHIDDevice.h b/libraries/BLE/src/BLEHIDDevice.h index 9dde9452c12..a34807536d7 100644 --- a/libraries/BLE/src/BLEHIDDevice.h +++ b/libraries/BLE/src/BLEHIDDevice.h @@ -13,9 +13,8 @@ #define _BLEHIDDEVICE_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "BLECharacteristic.h" @@ -80,5 +79,6 @@ class BLEHIDDevice { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* _BLEHIDDEVICE_H_ */ diff --git a/libraries/BLE/src/BLERemoteCharacteristic.cpp b/libraries/BLE/src/BLERemoteCharacteristic.cpp index aec9500d6f3..d26e2970a10 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.cpp +++ b/libraries/BLE/src/BLERemoteCharacteristic.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -846,7 +845,7 @@ String BLERemoteCharacteristic::readValue() { case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -928,10 +927,10 @@ bool BLERemoteCharacteristic::writeValue(uint8_t *data, size_t length, bool resp case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } - /* Else falls through. */ + /* Else falls through. */ default: goto exit; } } while (rc != 0 && retryCount--); @@ -999,4 +998,4 @@ bool BLERemoteCharacteristic::unsubscribe(bool response) { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteCharacteristic.h b/libraries/BLE/src/BLERemoteCharacteristic.h index 81ad7b2f4f5..5191087d11c 100644 --- a/libraries/BLE/src/BLERemoteCharacteristic.h +++ b/libraries/BLE/src/BLERemoteCharacteristic.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -45,6 +45,7 @@ #include #include +// Bluedroid Compatibility #define ESP_GATT_MAX_ATTR_LEN BLE_ATT_ATTR_MAX_LEN #define ESP_GATT_CHAR_PROP_BIT_READ BLE_GATT_CHR_PROP_READ #define ESP_GATT_CHAR_PROP_BIT_WRITE BLE_GATT_CHR_PROP_WRITE @@ -53,6 +54,12 @@ #define ESP_GATT_CHAR_PROP_BIT_NOTIFY BLE_GATT_CHR_PROP_NOTIFY #define ESP_GATT_CHAR_PROP_BIT_INDICATE BLE_GATT_CHR_PROP_INDICATE +#define ESP_GATT_AUTH_REQ_NONE 0 +#define ESP_GATT_AUTH_REQ_NO_MITM 1 +#define ESP_GATT_AUTH_REQ_MITM 2 +#define ESP_GATT_AUTH_REQ_SIGNED_NO_MITM 3 +#define ESP_GATT_AUTH_REQ_SIGNED_MITM 4 + #endif /*************************************************************************** @@ -186,5 +193,6 @@ class BLERemoteCharacteristic { }; // BLERemoteCharacteristic #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTECHARACTERISTIC_H_ */ diff --git a/libraries/BLE/src/BLERemoteDescriptor.cpp b/libraries/BLE/src/BLERemoteDescriptor.cpp index a142fe11880..e7cd3af38e6 100644 --- a/libraries/BLE/src/BLERemoteDescriptor.cpp +++ b/libraries/BLE/src/BLERemoteDescriptor.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -313,7 +312,7 @@ String BLERemoteDescriptor::readValue() { case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -459,7 +458,7 @@ bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHEN): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_AUTHOR): case BLE_HS_ATT_ERR(BLE_ATT_ERR_INSUFFICIENT_ENC): - if (retryCount && pClient->secureConnection()) { + if (BLESecurity::m_securityEnabled && retryCount && pClient->secureConnection()) { break; } /* Else falls through. */ @@ -480,4 +479,4 @@ bool BLERemoteDescriptor::writeValue(uint8_t *data, size_t length, bool response #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteDescriptor.h b/libraries/BLE/src/BLERemoteDescriptor.h index afe113df551..ea55fb81536 100644 --- a/libraries/BLE/src/BLERemoteDescriptor.h +++ b/libraries/BLE/src/BLERemoteDescriptor.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -103,5 +103,6 @@ class BLERemoteDescriptor { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTEDESCRIPTOR_H_ */ diff --git a/libraries/BLE/src/BLERemoteService.cpp b/libraries/BLE/src/BLERemoteService.cpp index 7baf6908d40..3cec81676be 100644 --- a/libraries/BLE/src/BLERemoteService.cpp +++ b/libraries/BLE/src/BLERemoteService.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -424,4 +423,4 @@ int BLERemoteService::characteristicDiscCB(uint16_t conn_handle, const struct bl #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLERemoteService.h b/libraries/BLE/src/BLERemoteService.h index c93d91f6852..977ccc85432 100644 --- a/libraries/BLE/src/BLERemoteService.h +++ b/libraries/BLE/src/BLERemoteService.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ #define COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -128,5 +128,6 @@ class BLERemoteService { }; // BLERemoteService #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEREMOTESERVICE_H_ */ diff --git a/libraries/BLE/src/BLEScan.cpp b/libraries/BLE/src/BLEScan.cpp index 6d18bb2d751..54e60a2e423 100644 --- a/libraries/BLE/src/BLEScan.cpp +++ b/libraries/BLE/src/BLEScan.cpp @@ -13,9 +13,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -860,4 +859,4 @@ void BLEScan::onHostSync() { #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEScan.h b/libraries/BLE/src/BLEScan.h index 842b7144f06..327eb8c418d 100644 --- a/libraries/BLE/src/BLEScan.h +++ b/libraries/BLE/src/BLEScan.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESCAN_H_ #define COMPONENTS_CPP_UTILS_BLESCAN_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -233,5 +233,6 @@ class BLEPeriodicScanCallbacks { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESCAN_H_ */ diff --git a/libraries/BLE/src/BLESecurity.cpp b/libraries/BLE/src/BLESecurity.cpp index 978026a3809..7e9269e6000 100644 --- a/libraries/BLE/src/BLESecurity.cpp +++ b/libraries/BLE/src/BLESecurity.cpp @@ -10,17 +10,19 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#include "Arduino.h" #include "BLESecurity.h" #include "BLEUtils.h" +#include "BLEDevice.h" +#include "GeneralUtils.h" #include "esp32-hal-log.h" /*************************************************************************** @@ -35,17 +37,46 @@ * Common properties * ***************************************************************************/ +// If true, the security will be enforced on connection even if no security is needed +// TODO: Make this configurable without breaking Bluedroid/NimBLE compatibility +bool BLESecurity::m_forceSecurity = true; + +bool BLESecurity::m_securityEnabled = false; +bool BLESecurity::m_securityStarted = false; +bool BLESecurity::m_passkeySet = false; +bool BLESecurity::m_staticPasskey = true; +bool BLESecurity::m_regenOnConnect = false; +uint8_t BLESecurity::m_iocap = ESP_IO_CAP_NONE; +uint8_t BLESecurity::m_authReq = 0; +uint8_t BLESecurity::m_initKey = 0; +uint8_t BLESecurity::m_respKey = 0; uint32_t BLESecurity::m_passkey = BLE_SM_DEFAULT_PASSKEY; /*************************************************************************** - * Common functions * + * Bluedroid properties * ***************************************************************************/ -BLESecurity::BLESecurity() {} +#if defined(CONFIG_BLUEDROID_ENABLED) +uint8_t BLESecurity::m_keySize = 16; +esp_ble_sec_act_t BLESecurity::m_securityLevel; +#endif + +/*************************************************************************** + * Common functions * + ***************************************************************************/ -BLESecurity::~BLESecurity() {} +// This function initializes the BLESecurity class. +BLESecurity::BLESecurity() { + log_d("BLESecurity: Initializing"); + setKeySize(); + setInitEncryptionKey(); + setRespEncryptionKey(); + setCapability(ESP_IO_CAP_NONE); +} +// This function sets the authentication mode for the BLE security. void BLESecurity::setAuthenticationMode(uint8_t auth_req) { + log_d("setAuthenticationMode: auth_req=%d", auth_req); m_authReq = auth_req; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); @@ -56,7 +87,9 @@ void BLESecurity::setAuthenticationMode(uint8_t auth_req) { #endif } +// This function sets the Input/Output capability for the BLE security. void BLESecurity::setCapability(uint8_t iocap) { + log_d("setCapability: iocap=%d", iocap); m_iocap = iocap; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, sizeof(uint8_t)); @@ -65,7 +98,12 @@ void BLESecurity::setCapability(uint8_t iocap) { #endif } +// This sets the initiator key distribution flags. +// ESP_BLE_ENC_KEY_MASK indicates that the device should distribute the Encryption Key to the peer device. +// ESP_BLE_ID_KEY_MASK indicates that the device should distribute the Identity Key to the peer device. +// Both are set by default. void BLESecurity::setInitEncryptionKey(uint8_t init_key) { + log_d("setInitEncryptionKey: init_key=%d", init_key); m_initKey = init_key; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &m_initKey, sizeof(uint8_t)); @@ -74,7 +112,12 @@ void BLESecurity::setInitEncryptionKey(uint8_t init_key) { #endif } +// This sets the responder key distribution flags. +// ESP_BLE_ENC_KEY_MASK indicates that the device should distribute the Encryption Key to the peer device. +// ESP_BLE_ID_KEY_MASK indicates that the device should distribute the Identity Key to the peer device. +// Both are set by default. void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { + log_d("setRespEncryptionKey: resp_key=%d", resp_key); m_respKey = resp_key; #if defined(CONFIG_BLUEDROID_ENABLED) esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &m_respKey, sizeof(uint8_t)); @@ -83,34 +126,188 @@ void BLESecurity::setRespEncryptionKey(uint8_t resp_key) { #endif } +// This function sets the key size for the BLE security. void BLESecurity::setKeySize(uint8_t key_size) { #if defined(CONFIG_BLUEDROID_ENABLED) + log_d("setKeySize: key_size=%d", key_size); m_keySize = key_size; esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &m_keySize, sizeof(uint8_t)); #endif } -void BLESecurity::setStaticPIN(uint32_t pin) { - m_passkey = pin; +// This function generates a random passkey between 000000 and 999999. +uint32_t BLESecurity::generateRandomPassKey() { + return random(0, 999999); +} + +// This function sets a passkey for the BLE security. +// The first argument defines if the passkey is static or random. +// The second argument is the passkey (ignored when using a random passkey). +// The function returns the passkey that was set. +uint32_t BLESecurity::setPassKey(bool staticPasskey, uint32_t passkey) { + log_d("setPassKey: staticPasskey=%d, passkey=%d", staticPasskey, passkey); + m_staticPasskey = staticPasskey; + + if (m_staticPasskey) { + m_passkey = passkey; + if (m_passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*WARNING* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*WARNING* Please use a random passkey or set a different static passkey"); + } + } else { + m_passkey = generateRandomPassKey(); + } + + m_passkeySet = true; + #if defined(CONFIG_BLUEDROID_ENABLED) + // Workaround for making Bluedroid and NimBLE manage the random passkey similarly. esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &m_passkey, sizeof(uint32_t)); - setCapability(ESP_IO_CAP_OUT); - setKeySize(); - setAuthenticationMode(ESP_LE_AUTH_REQ_SC_ONLY); - setInitEncryptionKey(ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK); +#endif + + return m_passkey; +} + +// This function gets the passkey being used for the BLE security. +// If a static passkey is set, it will return the static passkey. +// If using a random passkey, it will generate a new random passkey if m_regenOnConnect is true. +// Otherwise, it will return the current passkey being used. +uint32_t BLESecurity::getPassKey() { + if (m_passkeySet && !m_staticPasskey && m_regenOnConnect) { + m_passkey = generateRandomPassKey(); +#if defined(CONFIG_BLUEDROID_ENABLED) + esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &m_passkey, sizeof(uint32_t)); +#endif + } + return m_passkey; +} + +// This function sets if the passkey should be regenerated on each connection. +void BLESecurity::regenPassKeyOnConnect(bool enable) { + m_regenOnConnect = enable; +} + +// This function resets the security state on disconnect. +void BLESecurity::resetSecurity() { + log_d("resetSecurity: Resetting security state"); + m_securityStarted = false; +} + +// This function sets the authentication mode with bonding, MITM, and secure connection options. +void BLESecurity::setAuthenticationMode(bool bonding, bool mitm, bool sc) { + log_d("setAuthenticationMode: bonding=%d, mitm=%d, sc=%d", bonding, mitm, sc); + m_authReq = bonding ? ESP_LE_AUTH_BOND : 0; + m_authReq |= mitm ? ESP_LE_AUTH_REQ_MITM : 0; + m_authReq |= sc ? ESP_LE_AUTH_REQ_SC_ONLY : 0; + m_securityEnabled = (m_authReq != 0); +#if defined(CONFIG_BLUEDROID_ENABLED) + esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &m_authReq, sizeof(uint8_t)); + if (sc) { + if (mitm) { + setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_MITM); + } else { + setEncryptionLevel(ESP_BLE_SEC_ENCRYPT_NO_MITM); + } + } #elif defined(CONFIG_NIMBLE_ENABLED) - setCapability(BLE_HS_IO_DISPLAY_ONLY); - setKeySize(); - setAuthenticationMode(false, false, true); // No bonding, no MITM, secure connection only - setInitEncryptionKey(BLE_HS_KEY_DIST_ENC_KEY | BLE_HS_KEY_DIST_ID_KEY); + ble_hs_cfg.sm_bonding = bonding; + ble_hs_cfg.sm_mitm = mitm; + ble_hs_cfg.sm_sc = sc; #endif } +// This callback is called by the device that has Input capability when the peer device has Output capability +// It can also be called in NimBLE when there is no passkey set. +// It should return the passkey that the peer device is showing on its output. +// This might not be called if the client has a static passkey set. +uint32_t BLESecurityCallbacks::onPassKeyRequest() { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onPassKeyRequest."); + Serial.println("BLESecurityCallbacks: *ATTENTION* Please implement onPassKeyRequest with a suitable passkey in your BLESecurityCallbacks class"); + Serial.printf("BLESecurityCallbacks: Default passkey: %06d\n", BLE_SM_DEFAULT_PASSKEY); + return BLE_SM_DEFAULT_PASSKEY; +} + +// This callback is called by the device that has Output capability when the peer device has Input capability +// It should display the passkey that will need to be entered on the peer device +void BLESecurityCallbacks::onPassKeyNotify(uint32_t passkey) { + Serial.printf("BLESecurityCallbacks: Using default onPassKeyNotify. Passkey: %06lu\n", passkey); +} + +// This callback is called when the peer device requests a secure connection. +// Usually the client accepts the server's security request. +// It should return true if the connection is accepted, false otherwise. +bool BLESecurityCallbacks::onSecurityRequest() { + Serial.println("BLESecurityCallbacks: Using default onSecurityRequest. It will accept any security request."); + return true; +} + +// This callback is called by both devices when both have the DisplayYesNo capability. +// It should return true if both devices display the same passkey. +bool BLESecurityCallbacks::onConfirmPIN(uint32_t pin) { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onConfirmPIN. It will accept any passkey."); + Serial.println("BLESecurityCallbacks: *ATTENTION* Please implement onConfirmPIN with a suitable confirmation logic in your BLESecurityCallbacks class"); + return true; +} + +// This callback is called when the characteristic requires authorization. +// connHandle is the connection handle of the peer device. +// attrHandle is the handle of the characteristic. +// If isRead is true, the peer device is requesting to read the characteristic, +// otherwise it is requesting to write. +// It should return true if the authorization is granted, false otherwise. +bool BLESecurityCallbacks::onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) { + Serial.println("BLESecurityCallbacks: *ATTENTION* Using insecure onAuthorizationRequest. It will accept any authorization request."); + Serial.println( + "BLESecurityCallbacks: *ATTENTION* Please implement onAuthorizationRequest with a suitable authorization logic in your BLESecurityCallbacks class" + ); + return true; +} + /*************************************************************************** * Bluedroid functions * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) +// This function sets the encryption level that will be negotiated with peer device during connection +void BLESecurity::setEncryptionLevel(esp_ble_sec_act_t level) { + m_securityLevel = level; +} + +bool BLESecurity::startSecurity(esp_bd_addr_t bd_addr, int *rcPtr) { +#ifdef CONFIG_BLE_SMP_ENABLE + log_d("startSecurity: bd_addr=%s", BLEAddress(bd_addr).toString().c_str()); + if (m_securityStarted) { + log_w("Security already started for bd_addr=%s", BLEAddress(bd_addr).toString().c_str()); + if (rcPtr) { + *rcPtr = ESP_OK; + } + return true; + } + + if (m_securityEnabled) { + int rc = esp_ble_set_encryption(bd_addr, m_securityLevel); + if (rc != ESP_OK) { + log_e("esp_ble_set_encryption: rc=%d %s", rc, GeneralUtils::errorToString(rc)); + } + if (rcPtr) { + *rcPtr = rc; + } + m_securityStarted = (rc == ESP_OK); + } else { + log_e("Security is not enabled. Can't start security."); + if (rcPtr) { + *rcPtr = ESP_FAIL; + } + return false; + } + return m_securityStarted; +#else + log_e("Bluedroid SMP is not enabled. Can't start security."); + return false; +#endif +} + +// This function converts an ESP BLE key type to a string representation. char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { char *key_str = nullptr; switch (key_type) { @@ -127,6 +324,12 @@ char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { } return key_str; } + +// This function is called when authentication is complete. +void BLESecurityCallbacks::onAuthenticationComplete(esp_ble_auth_cmpl_t param) { + bool success = param.success; + Serial.printf("Using default onAuthenticationComplete. Authentication %s.\n", success ? "successful" : "failed"); +} #endif /*************************************************************************** @@ -134,26 +337,42 @@ char *BLESecurity::esp_key_type_to_str(esp_ble_key_type_t key_type) { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) -void BLESecurity::setAuthenticationMode(bool bonding, bool mitm, bool sc) { - m_authReq = bonding ? BLE_SM_PAIR_AUTHREQ_BOND : 0; - m_authReq |= mitm ? BLE_SM_PAIR_AUTHREQ_MITM : 0; - m_authReq |= sc ? BLE_SM_PAIR_AUTHREQ_SC : 0; - ble_hs_cfg.sm_bonding = bonding; - ble_hs_cfg.sm_mitm = mitm; - ble_hs_cfg.sm_sc = sc; -} - +// This function initiates security for a given connection handle. bool BLESecurity::startSecurity(uint16_t connHandle, int *rcPtr) { - int rc = ble_gap_security_initiate(connHandle); - if (rc != 0) { - log_e("ble_gap_security_initiate: rc=%d %s", rc, BLEUtils::returnCodeToString(rc)); + log_d("startSecurity: connHandle=%d", connHandle); + if (m_securityStarted) { + log_w("Security already started for connHandle=%d", connHandle); + if (rcPtr) { + *rcPtr = 0; + } + return true; } - if (rcPtr) { - *rcPtr = rc; + + if (m_securityEnabled) { + int rc = ble_gap_security_initiate(connHandle); + if (rc != 0) { + log_e("ble_gap_security_initiate: rc=%d %s", rc, BLEUtils::returnCodeToString(rc)); + } + if (rcPtr) { + *rcPtr = rc; + } + m_securityStarted = (rc == 0 || rc == BLE_HS_EALREADY); + } else { + log_e("Security is not enabled. Can't start security."); + if (rcPtr) { + *rcPtr = ESP_FAIL; + } + return false; } - return rc == 0 || rc == BLE_HS_EALREADY; + return m_securityStarted; +} + +// This function is called when authentication is complete for NimBLE. +void BLESecurityCallbacks::onAuthenticationComplete(ble_gap_conn_desc *desc) { + bool success = desc != nullptr; + Serial.printf("Using default onAuthenticationComplete. Authentication %s.\n", success ? "successful" : "failed"); } #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLESecurity.h b/libraries/BLE/src/BLESecurity.h index 574110e6118..2d2306231ac 100644 --- a/libraries/BLE/src/BLESecurity.h +++ b/libraries/BLE/src/BLESecurity.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLESECURITY_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** @@ -23,6 +22,9 @@ ***************************************************************************/ #include "WString.h" +#include "BLEDevice.h" +#include "BLEClient.h" +#include "BLEServer.h" /*************************************************************************** * Bluedroid includes * @@ -41,15 +43,42 @@ #endif /*************************************************************************** - * Common definitions * + * Common definitions * ***************************************************************************/ #define BLE_SM_DEFAULT_PASSKEY 123456 +/*************************************************************************** + * NimBLE definitions * + ***************************************************************************/ + +#if defined(CONFIG_NIMBLE_ENABLED) +// Compatibility with Bluedroid definitions + +#define ESP_IO_CAP_OUT BLE_HS_IO_DISPLAY_ONLY +#define ESP_IO_CAP_IO BLE_HS_IO_DISPLAY_YESNO +#define ESP_IO_CAP_IN BLE_HS_IO_KEYBOARD_ONLY +#define ESP_IO_CAP_NONE BLE_HS_IO_NO_INPUT_OUTPUT +#define ESP_IO_CAP_KBDISP BLE_HS_IO_KEYBOARD_DISPLAY + +#define ESP_LE_AUTH_NO_BOND 0x00 +#define ESP_LE_AUTH_BOND BLE_SM_PAIR_AUTHREQ_BOND +#define ESP_LE_AUTH_REQ_MITM BLE_SM_PAIR_AUTHREQ_MITM +#define ESP_LE_AUTH_REQ_SC_ONLY BLE_SM_PAIR_AUTHREQ_SC +#define ESP_LE_AUTH_REQ_BOND_MITM (BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_MITM) +#define ESP_LE_AUTH_REQ_SC_BOND (BLE_SM_PAIR_AUTHREQ_BOND | BLE_SM_PAIR_AUTHREQ_SC) +#define ESP_LE_AUTH_REQ_SC_MITM (BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC) +#define ESP_LE_AUTH_REQ_SC_MITM_BOND (BLE_SM_PAIR_AUTHREQ_MITM | BLE_SM_PAIR_AUTHREQ_SC | BLE_SM_PAIR_AUTHREQ_BOND) + +#define ESP_BLE_ENC_KEY_MASK BLE_HS_KEY_DIST_ENC_KEY +#define ESP_BLE_ID_KEY_MASK BLE_HS_KEY_DIST_ID_KEY +#endif + /*************************************************************************** * Forward declarations * ***************************************************************************/ +class BLEDevice; class BLEServer; class BLEClient; @@ -63,13 +92,18 @@ class BLESecurity { ***************************************************************************/ BLESecurity(); - virtual ~BLESecurity(); + virtual ~BLESecurity() = default; static void setAuthenticationMode(uint8_t auth_req); static void setCapability(uint8_t iocap); - static void setInitEncryptionKey(uint8_t init_key); - static void setRespEncryptionKey(uint8_t resp_key); + static void setInitEncryptionKey(uint8_t init_key = (ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK)); + static void setRespEncryptionKey(uint8_t resp_key = (ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK)); static void setKeySize(uint8_t key_size = 16); - static void setStaticPIN(uint32_t pin); + static uint32_t setPassKey(bool staticPasskey = false, uint32_t passkey = BLE_SM_DEFAULT_PASSKEY); + static void setAuthenticationMode(bool bonding, bool mitm, bool sc); + static uint32_t getPassKey(); + static uint32_t generateRandomPassKey(); + static void regenPassKeyOnConnect(bool enable = false); + static void resetSecurity(); /*************************************************************************** * Bluedroid public declarations * @@ -77,6 +111,8 @@ class BLESecurity { #if defined(CONFIG_BLUEDROID_ENABLED) static char *esp_key_type_to_str(esp_ble_key_type_t key_type); + static void setEncryptionLevel(esp_ble_sec_act_t level); + static bool startSecurity(esp_bd_addr_t bd_addr, int *rcPtr = nullptr); #endif /*************************************************************************** @@ -84,18 +120,26 @@ class BLESecurity { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) - static void setAuthenticationMode(bool bonding, bool mitm, bool sc); static bool startSecurity(uint16_t connHandle, int *rcPtr = nullptr); #endif private: + friend class BLEDevice; friend class BLEServer; friend class BLEClient; + friend class BLERemoteCharacteristic; + friend class BLERemoteDescriptor; /*************************************************************************** * Common private properties * ***************************************************************************/ + static bool m_securityEnabled; + static bool m_securityStarted; + static bool m_forceSecurity; + static bool m_passkeySet; + static bool m_staticPasskey; + static bool m_regenOnConnect; static uint8_t m_iocap; static uint8_t m_authReq; static uint8_t m_initKey; @@ -103,11 +147,12 @@ class BLESecurity { static uint32_t m_passkey; /*************************************************************************** - * Bluedroid private properties * + * Bluedroid private properties * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) static uint8_t m_keySize; + static esp_ble_sec_act_t m_securityLevel; #endif }; // BLESecurity @@ -121,18 +166,42 @@ class BLESecurityCallbacks { * Common public declarations * ***************************************************************************/ - virtual ~BLESecurityCallbacks(){}; - virtual uint32_t onPassKeyRequest() = 0; - virtual void onPassKeyNotify(uint32_t pass_key) = 0; - virtual bool onSecurityRequest() = 0; - virtual bool onConfirmPIN(uint32_t pin) = 0; + BLESecurityCallbacks() = default; + virtual ~BLESecurityCallbacks() = default; + + // This callback is called by the device that has Input capability when the peer device has Output capability + // and the passkey is not set. + // It should return the passkey that the peer device is showing on its output. + // This MUST be replaced with a custom implementation when being used. + virtual uint32_t onPassKeyRequest(); + + // This callback is called by the device that has Output capability when the peer device has Input capability + // It should display the passkey that will need to be entered on the peer device + virtual void onPassKeyNotify(uint32_t pass_key); + + // This callback is called when the peer device requests a secure connection. + // Usually the client accepts the server's security request. + // It should return true if the connection is accepted, false otherwise. + virtual bool onSecurityRequest(); + + // This callback is called by both devices when both have the DisplayYesNo capability. + // It should return true if both devices display the same passkey. + // This MUST be replaced with a custom implementation when being used. + virtual bool onConfirmPIN(uint32_t pin); + + // This callback is called when the peer device requests authorization to read or write a characteristic. + // It should return true if the authorization is granted, false otherwise. + // This MUST be replaced with a custom implementation when being used. + virtual bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead); /*************************************************************************** * Bluedroid public declarations * ***************************************************************************/ #if defined(CONFIG_BLUEDROID_ENABLED) - virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t) = 0; + // This callback is called when the authentication is complete. + // Status can be checked in the desc parameter. + virtual void onAuthenticationComplete(esp_ble_auth_cmpl_t desc); #endif /*************************************************************************** @@ -140,11 +209,14 @@ class BLESecurityCallbacks { ***************************************************************************/ #if defined(CONFIG_NIMBLE_ENABLED) - virtual void onAuthenticationComplete(ble_gap_conn_desc *) = 0; + // This callback is called when the authentication is complete. + // Status can be checked in the desc parameter. + virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); #endif }; // BLESecurityCallbacks -#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif // COMPONENTS_CPP_UTILS_BLESECURITY_H_ diff --git a/libraries/BLE/src/BLEServer.cpp b/libraries/BLE/src/BLEServer.cpp index 323237e965e..b25aed2de16 100644 --- a/libraries/BLE/src/BLEServer.cpp +++ b/libraries/BLE/src/BLEServer.cpp @@ -10,16 +10,17 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /*************************************************************************** * Common includes * ***************************************************************************/ +#if SOC_BLE_SUPPORTED #include +#endif #include "GeneralUtils.h" #include "BLEDevice.h" #include "BLEServer.h" @@ -67,6 +68,10 @@ BLEServer::BLEServer() { m_svcChanged = false; #endif +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + m_advertiseOnDisconnect = false; +#endif + m_appId = ESP_GATT_IF_NONE; m_gattsStarted = false; m_connectedCount = 0; @@ -296,6 +301,12 @@ bool BLEServer::removePeerDevice(uint16_t conn_id, bool _client) { return m_connectedServersMap.erase(conn_id) > 0; } +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) +void BLEServer::advertiseOnDisconnect(bool enable) { + m_advertiseOnDisconnect = enable; +} +#endif + void BLEServerCallbacks::onConnect(BLEServer *pServer) { log_d("BLEServerCallbacks", ">> onConnect(): Default"); log_d("BLEServerCallbacks", "Device: %s", BLEDevice::toString().c_str()); @@ -449,6 +460,16 @@ void BLEServer::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t if (removePeerDevice(param->disconnect.conn_id, false)) { m_connectedCount--; // Decrement the number of connected devices count. } + + // Reset security state on disconnect + BLESecurity::resetSecurity(); + + // Start advertising again if enabled + if (m_advertiseOnDisconnect) { + log_i("Start advertising again after disconnect"); + startAdvertising(); + } + break; } // ESP_GATTS_DISCONNECT_EVT @@ -621,6 +642,10 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { server->m_pServerCallbacks->onConnect(server, &desc); } + if (BLESecurity::m_securityEnabled && BLESecurity::m_forceSecurity) { + BLESecurity::startSecurity(event->connect.conn_handle); + } + server->m_connectedCount++; } @@ -655,6 +680,16 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { server->m_pServerCallbacks->onDisconnect(server, &event->disconnect.conn); } + // Reset security state on disconnect + BLESecurity::resetSecurity(); + +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) + if (server->m_advertiseOnDisconnect) { + log_i("Start advertising again after disconnect"); + server->startAdvertising(); + } +#endif + return 0; } // BLE_GAP_EVENT_DISCONNECT @@ -784,8 +819,6 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { if (BLEDevice::m_securityCallbacks != nullptr) { BLEDevice::m_securityCallbacks->onAuthenticationComplete(&desc); - } else if (server->m_pServerCallbacks != nullptr) { - server->m_pServerCallbacks->onAuthenticationComplete(&desc); } return 0; @@ -796,63 +829,124 @@ int BLEServer::handleGATTServerEvent(struct ble_gap_event *event, void *arg) { struct ble_sm_io pkey = {0, 0}; if (event->passkey.params.action == BLE_SM_IOACT_DISP) { + // Display the passkey on this device + log_d("BLE_SM_IOACT_DISP"); + pkey.action = event->passkey.params.action; - // backward compatibility - pkey.passkey = BLESecurity::m_passkey; - // if the (static)passkey is the default, check the callback for custom value - // both values default to the same. - if (pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { - if (server->m_pServerCallbacks != nullptr) { - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); - } + pkey.passkey = BLESecurity::getPassKey(); + + if (!BLESecurity::m_passkeySet) { + log_w("No passkey set"); } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); + } + + if (BLEDevice::m_securityCallbacks != nullptr) { + BLEDevice::m_securityCallbacks->onPassKeyNotify(pkey.passkey); + } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_DISP; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) { + // Check if the passkey on the peer device is correct + log_d("BLE_SM_IOACT_NUMCMP"); + log_d("Passkey on device's display: %d", event->passkey.params.numcmp); pkey.action = event->passkey.params.action; - // Compatibility only - Do not use, should be removed the in future + if (BLEDevice::m_securityCallbacks != nullptr) { pkey.numcmp_accept = BLEDevice::m_securityCallbacks->onConfirmPIN(event->passkey.params.numcmp); - } else if (server->m_pServerCallbacks != nullptr) { - pkey.numcmp_accept = server->m_pServerCallbacks->onConfirmPIN(event->passkey.params.numcmp); + } else { + log_e("onConfirmPIN not implemented. Rejecting connection"); + pkey.numcmp_accept = 0; } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_NUMCMP; ble_sm_inject_io result: %d", rc); - //TODO: Handle out of band pairing } else if (event->passkey.params.action == BLE_SM_IOACT_OOB) { + // Out of band pairing + // TODO: Handle out of band pairing + log_w("BLE_SM_IOACT_OOB: Not implemented"); + static uint8_t tem_oob[16] = {0}; pkey.action = event->passkey.params.action; for (int i = 0; i < 16; i++) { pkey.oob[i] = tem_oob[i]; } + rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_OOB; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) { - log_d("Enter the passkey"); + // Input passkey from peer device + log_d("BLE_SM_IOACT_INPUT"); + pkey.action = event->passkey.params.action; + pkey.passkey = BLESecurity::getPassKey(); - // Compatibility only - Do not use, should be removed the in future - if (BLEDevice::m_securityCallbacks != nullptr) { - pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); - } else if (server->m_pServerCallbacks != nullptr) { - pkey.passkey = server->m_pServerCallbacks->onPassKeyRequest(); + if (!BLESecurity::m_passkeySet) { + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("No passkey set, getting passkey from onPassKeyRequest"); + pkey.passkey = BLEDevice::m_securityCallbacks->onPassKeyRequest(); + } else { + log_w("*ATTENTION* onPassKeyRequest not implemented and no static passkey set."); + } + } + + if (BLESecurity::m_staticPasskey && pkey.passkey == BLE_SM_DEFAULT_PASSKEY) { + log_w("*ATTENTION* Using default passkey: %06d", BLE_SM_DEFAULT_PASSKEY); + log_w("*ATTENTION* Please use a random passkey or set a different static passkey"); + } else { + log_i("Passkey: %d", pkey.passkey); } rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey); log_d("BLE_SM_IOACT_INPUT; ble_sm_inject_io result: %d", rc); } else if (event->passkey.params.action == BLE_SM_IOACT_NONE) { - log_d("No passkey action required"); + log_d("BLE_SM_IOACT_NONE"); + log_i("No passkey action required"); } log_d("<< handleGATTServerEvent"); return 0; } // BLE_GAP_EVENT_PASSKEY_ACTION + case BLE_GAP_EVENT_AUTHORIZE: + { + log_d("BLE_GAP_EVENT_AUTHORIZE"); + + log_i( + "Authorization request: conn_handle=%d attr_handle=%d is_read=%d", event->authorize.conn_handle, event->authorize.attr_handle, event->authorize.is_read + ); + + bool authorized = false; + + if (BLEDevice::m_securityCallbacks != nullptr) { + log_i("Asking for authorization from onAuthorizationRequest"); + authorized = + BLEDevice::m_securityCallbacks->onAuthorizationRequest(event->authorize.conn_handle, event->authorize.attr_handle, event->authorize.is_read); + } else { + log_w("onAuthorizationRequest not implemented. Rejecting authorization request"); + } + + if (authorized) { + log_i("Authorization granted"); + event->authorize.out_response = BLE_GAP_AUTHORIZE_ACCEPT; + } else { + log_i("Authorization rejected"); + event->authorize.out_response = BLE_GAP_AUTHORIZE_REJECT; + } + + return 0; + } // BLE_GAP_EVENT_AUTHORIZE + default: break; } @@ -951,21 +1045,7 @@ void BLEServerCallbacks::onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *des log_d("BLEServerCallbacks", "<< onMtuChanged()"); } // onMtuChanged -uint32_t BLEServerCallbacks::onPassKeyRequest() { - log_d("BLEServerCallbacks", "onPassKeyRequest: default: 123456"); - return 123456; -} - -void BLEServerCallbacks::onAuthenticationComplete(ble_gap_conn_desc *) { - log_d("BLEServerCallbacks", "onAuthenticationComplete: default"); -} - -bool BLEServerCallbacks::onConfirmPIN(uint32_t pin) { - log_d("BLEServerCallbacks", "onConfirmPIN: default: true"); - return true; -} - #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEServer.h b/libraries/BLE/src/BLEServer.h index 865d1046312..c385c22cc98 100644 --- a/libraries/BLE/src/BLEServer.h +++ b/libraries/BLE/src/BLEServer.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESERVER_H_ #define COMPONENTS_CPP_UTILS_BLESERVER_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -129,6 +129,9 @@ class BLEServer { BLEService *getServiceByUUID(const char *uuid); BLEService *getServiceByUUID(BLEUUID uuid); void start(); +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + void advertiseOnDisconnect(bool enable); +#endif // Connection management functions std::map getPeerDevices(bool client); @@ -174,6 +177,9 @@ class BLEServer { uint32_t m_connectedCount; bool m_gattsStarted; std::map m_connectedServersMap; +#if !defined(CONFIG_BT_NIMBLE_EXT_ADV) || defined(CONFIG_BLUEDROID_ENABLED) + bool m_advertiseOnDisconnect; +#endif FreeRTOS::Semaphore m_semaphoreRegisterAppEvt = FreeRTOS::Semaphore("RegisterAppEvt"); FreeRTOS::Semaphore m_semaphoreCreateEvt = FreeRTOS::Semaphore("CreateEvt"); FreeRTOS::Semaphore m_semaphoreOpenEvt = FreeRTOS::Semaphore("OpenEvt"); @@ -261,12 +267,10 @@ class BLEServerCallbacks { virtual void onConnect(BLEServer *pServer, ble_gap_conn_desc *desc); virtual void onDisconnect(BLEServer *pServer, ble_gap_conn_desc *desc); virtual void onMtuChanged(BLEServer *pServer, ble_gap_conn_desc *desc, uint16_t mtu); - virtual uint32_t onPassKeyRequest(); - virtual void onAuthenticationComplete(ble_gap_conn_desc *desc); - virtual bool onConfirmPIN(uint32_t pin); #endif }; // BLEServerCallbacks #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESERVER_H_ */ diff --git a/libraries/BLE/src/BLEService.cpp b/libraries/BLE/src/BLEService.cpp index 60449719b6a..438c9aa6a1b 100644 --- a/libraries/BLE/src/BLEService.cpp +++ b/libraries/BLE/src/BLEService.cpp @@ -12,9 +12,8 @@ // A service is identified by a UUID. A service is also the container for one or more characteristics. #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -650,4 +649,4 @@ bool BLEService::start() { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEService.h b/libraries/BLE/src/BLEService.h index 67088b60310..a3d3f14b2cd 100644 --- a/libraries/BLE/src/BLEService.h +++ b/libraries/BLE/src/BLEService.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLESERVICE_H_ #define COMPONENTS_CPP_UTILS_BLESERVICE_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -61,13 +61,13 @@ class BLECharacteristicMap { void setByUUID(BLECharacteristic *pCharacteristic, const char *uuid); void setByUUID(BLECharacteristic *pCharacteristic, BLEUUID uuid); void setByHandle(uint16_t handle, BLECharacteristic *pCharacteristic); - BLECharacteristic *getByUUID(const char *uuid); - BLECharacteristic *getByUUID(BLEUUID uuid); - BLECharacteristic *getByHandle(uint16_t handle); + BLECharacteristic *getByUUID(const char *uuid) const; + BLECharacteristic *getByUUID(BLEUUID uuid) const; + BLECharacteristic *getByHandle(uint16_t handle) const; BLECharacteristic *getFirst(); BLECharacteristic *getNext(); - String toString(); - int getRegisteredCharacteristicCount(); + String toString() const; + int getRegisteredCharacteristicCount() const; void removeCharacteristic(BLECharacteristic *characteristic); /*************************************************************************** @@ -197,5 +197,6 @@ class BLEService { }; // BLEService #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLESERVICE_H_ */ diff --git a/libraries/BLE/src/BLEServiceMap.cpp b/libraries/BLE/src/BLEServiceMap.cpp index 477da5b4cc2..ee58aa284e3 100644 --- a/libraries/BLE/src/BLEServiceMap.cpp +++ b/libraries/BLE/src/BLEServiceMap.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -154,4 +153,4 @@ void BLEServiceMap::handleGATTServerEvent(esp_gatts_cb_event_t event, esp_gatt_i #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUUID.cpp b/libraries/BLE/src/BLEUUID.cpp index d61fb695799..5dd3833b0e4 100644 --- a/libraries/BLE/src/BLEUUID.cpp +++ b/libraries/BLE/src/BLEUUID.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -333,4 +332,4 @@ const ble_uuid_any_t *BLEUUID::getNative() const { #endif #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUUID.h b/libraries/BLE/src/BLEUUID.h index 43c4e91b01d..da7c38928f3 100644 --- a/libraries/BLE/src/BLEUUID.h +++ b/libraries/BLE/src/BLEUUID.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEUUID_H_ #define COMPONENTS_CPP_UTILS_BLEUUID_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -123,5 +123,6 @@ class BLEUUID { }; // BLEUUID #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEUUID_H_ */ diff --git a/libraries/BLE/src/BLEUtils.cpp b/libraries/BLE/src/BLEUtils.cpp index 4ca04e6e2b6..f4b3ac6d217 100644 --- a/libraries/BLE/src/BLEUtils.cpp +++ b/libraries/BLE/src/BLEUtils.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -27,7 +26,9 @@ #include #include +#if SOC_BLE_SUPPORTED #include +#endif #include #include @@ -656,7 +657,7 @@ static const gattService_t g_gattServices[] = { * @param [in] length The length of the data to convert. * @return A pointer to the formatted buffer. */ -char *BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { +char *BLEUtils::buildHexData(uint8_t *target, const uint8_t *source, uint8_t length) { // Guard against too much data. if (length > 100) { length = 100; @@ -672,8 +673,7 @@ char *BLEUtils::buildHexData(uint8_t *target, uint8_t *source, uint8_t length) { char *startOfData = (char *)target; for (int i = 0; i < length; i++) { - sprintf((char *)target, "%.2x", (char)*source); - source++; + sprintf((char *)target, "%.2x", (char)source[i]); target += 2; } @@ -1568,7 +1568,7 @@ void BLEUtils::dumpGattServerEvent(esp_gatts_cb_event_t event, esp_gatt_if_t gat // - uint32_t trans_id // - esp_bd_addr_t bda // - uint8_t exec_write_flag -#ifdef ARDUHAL_LOG_LEVEL_VERBOSE +#if ARDUHAL_LOG_LEVEL >= ARDUHAL_LOG_LEVEL_VERBOSE case ESP_GATTS_EXEC_WRITE_EVT: { char *pWriteFlagText; @@ -2171,6 +2171,9 @@ const char *BLEUtils::gapEventToString(uint8_t eventType) { case BLE_GAP_EVENT_EXT_DISC: //19 return "BLE_GAP_EVENT_EXT_DISC"; + + case BLE_GAP_EVENT_AUTHORIZE: //32 + return "BLE_GAP_EVENT_AUTHORIZE"; #ifdef BLE_GAP_EVENT_PERIODIC_SYNC // IDF 4.0 does not support these case BLE_GAP_EVENT_PERIODIC_SYNC: //20 return "BLE_GAP_EVENT_PERIODIC_SYNC"; @@ -2254,4 +2257,4 @@ void BLEUtils::taskRelease(const BLETaskData &taskData, int flags) { #endif // CONFIG_NIMBLE_ENABLED #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEUtils.h b/libraries/BLE/src/BLEUtils.h index 2689706b7a1..f66cdcfcdb1 100644 --- a/libraries/BLE/src/BLEUtils.h +++ b/libraries/BLE/src/BLEUtils.h @@ -11,10 +11,10 @@ #ifndef COMPONENTS_CPP_UTILS_BLEUTILS_H_ #define COMPONENTS_CPP_UTILS_BLEUTILS_H_ -#include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED +#include "soc/soc_caps.h" #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -96,7 +96,7 @@ class BLEUtils { * Common public declarations * ***************************************************************************/ - static char *buildHexData(uint8_t *target, uint8_t *source, uint8_t length); + static char *buildHexData(uint8_t *target, const uint8_t *source, uint8_t length); static String buildPrintData(uint8_t *source, size_t length); static const char *advDataTypeToString(uint8_t advType); static String characteristicPropertiesToString(uint8_t prop); @@ -148,5 +148,6 @@ class BLEUtils { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEUTILS_H_ */ diff --git a/libraries/BLE/src/BLEValue.cpp b/libraries/BLE/src/BLEValue.cpp index efc97697baa..47a7b3d9a84 100644 --- a/libraries/BLE/src/BLEValue.cpp +++ b/libraries/BLE/src/BLEValue.cpp @@ -10,9 +10,8 @@ */ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -37,7 +36,7 @@ BLEValue::BLEValue() { * The accumulation is a growing set of data that is added to until a commit or cancel. * @param [in] part A message part being added. */ -void BLEValue::addPart(String part) { +void BLEValue::addPart(const String &part) { log_v(">> addPart: length=%d", part.length()); m_accumulation += part; } // addPart @@ -48,7 +47,7 @@ void BLEValue::addPart(String part) { * @param [in] pData A message part being added. * @param [in] length The number of bytes being added. */ -void BLEValue::addPart(uint8_t *pData, size_t length) { +void BLEValue::addPart(const uint8_t *pData, size_t length) { log_v(">> addPart: length=%d", length); m_accumulation += String((char *)pData, length); } // addPart @@ -91,7 +90,7 @@ uint8_t *BLEValue::getData() { * @brief Get the length of the data in bytes. * @return The length of the data in bytes. */ -size_t BLEValue::getLength() { +size_t BLEValue::getLength() const { return m_value.length(); } // getLength @@ -99,14 +98,14 @@ size_t BLEValue::getLength() { * @brief Get the read offset. * @return The read offset into the read. */ -uint16_t BLEValue::getReadOffset() { +uint16_t BLEValue::getReadOffset() const { return m_readOffset; } // getReadOffset /** * @brief Get the current value. */ -String BLEValue::getValue() { +String BLEValue::getValue() const { return m_value; } // getValue @@ -121,7 +120,7 @@ void BLEValue::setReadOffset(uint16_t readOffset) { /** * @brief Set the current value. */ -void BLEValue::setValue(String value) { +void BLEValue::setValue(const String &value) { m_value = value; } // setValue @@ -130,9 +129,9 @@ void BLEValue::setValue(String value) { * @param [in] pData The data for the current value. * @param [in] The length of the new current value. */ -void BLEValue::setValue(uint8_t *pData, size_t length) { +void BLEValue::setValue(const uint8_t *pData, size_t length) { m_value = String((char *)pData, length); } // setValue #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ diff --git a/libraries/BLE/src/BLEValue.h b/libraries/BLE/src/BLEValue.h index 56a7a5bc4ec..99147db0959 100644 --- a/libraries/BLE/src/BLEValue.h +++ b/libraries/BLE/src/BLEValue.h @@ -13,9 +13,8 @@ #define COMPONENTS_CPP_UTILS_BLEVALUE_H_ #include "soc/soc_caps.h" -#if SOC_BLE_SUPPORTED - #include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) #if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) /***************************************************************************** @@ -34,17 +33,17 @@ class BLEValue { ***************************************************************************/ BLEValue(); - void addPart(String part); - void addPart(uint8_t *pData, size_t length); + void addPart(const String &part); + void addPart(const uint8_t *pData, size_t length); void cancel(); void commit(); uint8_t *getData(); - size_t getLength(); - uint16_t getReadOffset(); - String getValue(); + size_t getLength() const; + uint16_t getReadOffset() const; + String getValue() const; void setReadOffset(uint16_t readOffset); - void setValue(String value); - void setValue(uint8_t *pData, size_t length); + void setValue(const String &value); + void setValue(const uint8_t *pData, size_t length); private: /*************************************************************************** @@ -57,5 +56,6 @@ class BLEValue { }; #endif /* CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED */ -#endif /* SOC_BLE_SUPPORTED */ +#endif /* SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE */ + #endif /* COMPONENTS_CPP_UTILS_BLEVALUE_H_ */ diff --git a/libraries/BluetoothSerial/src/BluetoothSerial.h b/libraries/BluetoothSerial/src/BluetoothSerial.h index 8cb6edb876c..68ddd3c747a 100644 --- a/libraries/BluetoothSerial/src/BluetoothSerial.h +++ b/libraries/BluetoothSerial/src/BluetoothSerial.h @@ -35,7 +35,7 @@ typedef std::function KeyRequestCb; typedef std::function AuthCompleteCb; typedef std::function BTAdvertisedDeviceCb; -class BluetoothSerial : public Stream { +class [[deprecated("BluetoothSerial won't be supported in version 4.0.0 by default")]] BluetoothSerial : public Stream { public: BluetoothSerial(void); ~BluetoothSerial(void); diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp index 016e6843dd2..05b9d3acd44 100644 --- a/libraries/EEPROM/src/EEPROM.cpp +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -27,6 +27,7 @@ #include #include #include +#include EEPROMClass::EEPROMClass(void) : _handle(0), _data(0), _size(0), _dirty(false), _name("eeprom") {} @@ -59,7 +60,7 @@ bool EEPROMClass::begin(size_t size) { } if (size < key_size) { // truncate log_w("truncating EEPROM from %d to %d", key_size, size); - uint8_t *key_data = (uint8_t *)malloc(key_size); + uint8_t *key_data = new (std::nothrow) uint8_t[key_size]; if (!key_data) { log_e("Not enough memory to truncate EEPROM!"); return false; @@ -67,10 +68,10 @@ bool EEPROMClass::begin(size_t size) { nvs_get_blob(_handle, _name, key_data, &key_size); nvs_set_blob(_handle, _name, key_data, size); nvs_commit(_handle); - free(key_data); + delete[] key_data; } else if (size > key_size) { // expand or new size_t expand_size = size - key_size; - uint8_t *expand_key = (uint8_t *)malloc(expand_size); + uint8_t *expand_key = new (std::nothrow) uint8_t[expand_size]; if (!expand_key) { log_e("Not enough memory to expand EEPROM!"); return false; @@ -78,12 +79,12 @@ bool EEPROMClass::begin(size_t size) { // check for adequate free space if (nvs_set_blob(_handle, "expand", expand_key, expand_size)) { log_e("Not enough space to expand EEPROM from %d to %d", key_size, size); - free(expand_key); + delete[] expand_key; return false; } - free(expand_key); + delete[] expand_key; nvs_erase_key(_handle, "expand"); - uint8_t *key_data = (uint8_t *)malloc(size); + uint8_t *key_data = new (std::nothrow) uint8_t[size]; if (!key_data) { log_e("Not enough memory to expand EEPROM!"); return false; @@ -99,7 +100,7 @@ bool EEPROMClass::begin(size_t size) { } nvs_commit(_handle); nvs_set_blob(_handle, _name, key_data, size); - free(key_data); + delete[] key_data; nvs_commit(_handle); } @@ -107,7 +108,7 @@ bool EEPROMClass::begin(size_t size) { delete[] _data; } - _data = (uint8_t *)malloc(size); + _data = new (std::nothrow) uint8_t[size]; if (!_data) { log_e("Not enough memory for %d bytes in EEPROM", size); return false; @@ -212,7 +213,7 @@ uint16_t EEPROMClass::convert(bool clear, const char *EEPROMname, const char *nv } size_t size = mypart->size; - uint8_t *data = (uint8_t *)malloc(size); + uint8_t *data = new (std::nothrow) uint8_t[size]; if (!data) { log_e("Not enough memory to convert EEPROM!"); goto exit; @@ -255,7 +256,7 @@ uint16_t EEPROMClass::convert(bool clear, const char *EEPROMname, const char *nv } } exit: - free(data); + delete[] data; return result; } @@ -509,9 +510,9 @@ size_t EEPROMClass::writeBytes(int address, const void *value, size_t len) { return len; } -template T EEPROMClass::writeAll(int address, const T &value) { +template size_t EEPROMClass::writeAll(int address, const T &value) { if (address < 0 || address + sizeof(T) > _size) { - return value; + return 0; } memcpy(_data + address, (const uint8_t *)&value, sizeof(T)); diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h index 2bcc97a3a21..45222b0c697 100644 --- a/libraries/EEPROM/src/EEPROM.h +++ b/libraries/EEPROM/src/EEPROM.h @@ -105,7 +105,7 @@ class EEPROMClass { size_t writeString(int address, const char *value); size_t writeString(int address, String value); size_t writeBytes(int address, const void *value, size_t len); - template T writeAll(int address, const T &); + template size_t writeAll(int address, const T &); protected: nvs_handle _handle; diff --git a/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino b/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino new file mode 100644 index 00000000000..7b23e259173 --- /dev/null +++ b/libraries/ESP32/examples/ArduinoWaitTimeBeforeStartingSketch/ArduinoWaitTimeBeforeStartingSketch.ino @@ -0,0 +1,13 @@ +// macro SET_TIME_BEFORE_STARTING_SKETCH_MS(time_ms) can set a time in milliseconds +// before the sketch would start its execution. It gives the user time to open the Serial Monitor + +// This will force the Sketch execution to wait for 5 seconds before starting it execution +// setup() will be executed only after this time +SET_TIME_BEFORE_STARTING_SKETCH_MS(5000); + +void setup() { + Serial.begin(115200); + Serial.println("After 5 seconds... this message will be seen in the Serial Monitor."); +} + +void loop() {} diff --git a/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino b/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino index f368e0e864c..9c90571493d 100644 --- a/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino +++ b/libraries/ESP32/examples/FreeRTOS/Mutex/Mutex.ino @@ -71,17 +71,17 @@ void Task(void *pvParameters) { // This is a task. #endif int new_value = random(1000); - char str0[32]; - sprintf(str0, " %d <- %d |", shared_variable, new_value); - char str1[32]; - sprintf(str1, " | %d <- %d", shared_variable, new_value); + char str0[35]; // Maximum possible length of the string + snprintf(str0, sizeof(str0), " %d <- %d |", shared_variable, new_value); + char str1[46]; // Maximum possible length of the string + snprintf(str1, sizeof(str1), " | %d <- %d", shared_variable, new_value); Serial.printf("%s\n", task_num ? str0 : str1); shared_variable = new_value; delay(random(100)); // wait random time of max 100 ms - simulating some computation - sprintf(str0, " R: %d |", shared_variable); - sprintf(str1, " | R: %d", shared_variable); + snprintf(str0, sizeof(str0), " R: %d |", shared_variable); + snprintf(str1, sizeof(str1), " | R: %d", shared_variable); Serial.printf("%s\n", task_num ? str0 : str1); //Serial.printf("Task %d after write: reading %d\n", task_num, shared_variable); diff --git a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino index 1cdd2224ea5..df906588479 100644 --- a/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino +++ b/libraries/ESP32/examples/RMT/RMT_LED_Blink/RMT_LED_Blink.ino @@ -276,6 +276,30 @@ void RMT_Loop_Write_Blink() { delay(2000); } +void RMT_Repeated_Write_Blink() { + Serial.println("Using RMT Writing repeated N times to blink an LED."); + Serial.println("Blinking at 1s on + 1s off :: 2 blinks"); + // repeating blink_1s_rmt_data (1s on + 1s off) 2 times for 2 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_1s_rmt_data, RMT_SYMBOLS_OF(blink_1s_rmt_data), 2)) { + Serial.println("===> rmtWrite Blink 1s Error!"); + } + delay(4000 + 1000); // it should blink 2 times and stop automatically + Serial.println("Blinking at 500ms on + 500ms off :: 4 blinks"); + // repeating blink_500ms_rmt_data (500ms on + 500ms off) 4 times for 4 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_500ms_rmt_data, RMT_SYMBOLS_OF(blink_500ms_rmt_data), 4)) { + Serial.println("===> rmtWrite Blink 0.5s Error!"); + } + delay(4000 + 1000); // it should blink 4 times and stop automatically + Serial.println("Blinking at 250ms on + 250ms off :: 8 blinks"); + // repeating blink_250ms_rmt_data (250ms on + 250ms off) 8 times for 8 blinks + if (!rmtWriteRepeated(BLINK_GPIO, blink_250ms_rmt_data, RMT_SYMBOLS_OF(blink_250ms_rmt_data), 8)) { + Serial.println("===> rmtWrite Blink 0.25s Error!"); + } + delay(4000 + 1000); // it should blink 8 times and stop automatically + Serial.println("Blinking is OFF for 2 seconds"); + delay(2000); +} + void RMT_Single_Write_Blocking_Blink() { Serial.println("Using RMT Writing and its Completion to blink an LED."); Serial.println("Blinking at 1s on + 1s off :: 2 blinks"); @@ -356,6 +380,7 @@ void setup() { void loop() { RMT_Write_Aync_Non_Blocking_Blink(); RMT_Loop_Write_Blink(); + RMT_Repeated_Write_Blink(); RMT_Single_Write_Blocking_Blink(); Serial.println("\nStarting OVER...\n"); } diff --git a/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino b/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino index 43f3bc36592..ac2b641be40 100644 --- a/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino +++ b/libraries/ESP32/examples/Touch/TouchButton/TouchButton.ino @@ -3,44 +3,53 @@ This is an example how to use Touch Intrrerupts The sketch will tell when it is touched and then released as like a push-button -This method based on touchInterruptSetThresholdDirection() is only available for ESP32 +This method based on touchInterruptGetLastStatus() */ #include "Arduino.h" -int threshold = 40; -bool touchActive = false; -bool lastTouchActive = false; -bool testingLower = true; - -void gotTouchEvent() { - if (lastTouchActive != testingLower) { - touchActive = !touchActive; - testingLower = !testingLower; - // Touch ISR will be inverted: Lower <--> Higher than the Threshold after ISR event is noticed - touchInterruptSetThresholdDirection(testingLower); - } +int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) +bool touch1detected = false; +bool touch2detected = false; + +void gotTouch1() { + touch1detected = true; +} + +void gotTouch2() { + touch2detected = true; } void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor - Serial.println("ESP32 Touch Interrupt Test"); - touchAttachInterrupt(T2, gotTouchEvent, threshold); - // Touch ISR will be activated when touchRead is lower than the Threshold - touchInterruptSetThresholdDirection(testingLower); + //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. + touchSetDefaultThreshold(5); + + //Set the touch pads + Serial.println("\n ESP32 Touch Interrupt Test\n"); + touchAttachInterrupt(T1, gotTouch1, threshold); + touchAttachInterrupt(T2, gotTouch2, threshold); } void loop() { - if (lastTouchActive != touchActive) { - lastTouchActive = touchActive; - if (touchActive) { - Serial.println(" ---- Touch was Pressed"); + if (touch1detected) { + touch1detected = false; + if (touchInterruptGetLastStatus(T1)) { + Serial.println(" --- T1 Touched"); } else { - Serial.println(" ---- Touch was Released"); + Serial.println(" --- T1 Released"); } } - Serial.printf("T2 pin2 = %d \n", touchRead(T2)); - delay(125); + if (touch2detected) { + touch2detected = false; + if (touchInterruptGetLastStatus(T2)) { + Serial.println(" --- T2 Touched"); + } else { + Serial.println(" --- T2 Released"); + } + } + + delay(80); } diff --git a/libraries/ESP32/examples/Touch/TouchButton/ci.json b/libraries/ESP32/examples/Touch/TouchButton/ci.json index cec76a84f9d..c0ecf9fc0a5 100644 --- a/libraries/ESP32/examples/Touch/TouchButton/ci.json +++ b/libraries/ESP32/examples/Touch/TouchButton/ci.json @@ -1,5 +1,5 @@ { "requires": [ - "CONFIG_SOC_TOUCH_VERSION_1=y" + "CONFIG_SOC_TOUCH_SENSOR_SUPPORTED=y" ] } diff --git a/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino b/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino deleted file mode 100644 index df9b3f41149..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButtonV2/TouchButtonV2.ino +++ /dev/null @@ -1,51 +0,0 @@ -/* - -This is an example how to use Touch Intrrerupts -The sketch will tell when it is touched and then released as like a push-button - -This method based on touchInterruptGetLastStatus() is only available for ESP32 S2 and S3 -*/ - -#include "Arduino.h" - -int threshold = 1500; // ESP32S2 -bool touch1detected = false; -bool touch2detected = false; - -void gotTouch1() { - touch1detected = true; -} - -void gotTouch2() { - touch2detected = true; -} - -void setup() { - Serial.begin(115200); - delay(1000); // give me time to bring up serial monitor - - Serial.println("\n ESP32 Touch Interrupt Test\n"); - touchAttachInterrupt(T1, gotTouch1, threshold); - touchAttachInterrupt(T2, gotTouch2, threshold); -} - -void loop() { - if (touch1detected) { - touch1detected = false; - if (touchInterruptGetLastStatus(T1)) { - Serial.println(" --- T1 Touched"); - } else { - Serial.println(" --- T1 Released"); - } - } - if (touch2detected) { - touch2detected = false; - if (touchInterruptGetLastStatus(T2)) { - Serial.println(" --- T2 Touched"); - } else { - Serial.println(" --- T2 Released"); - } - } - - delay(80); -} diff --git a/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json b/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json deleted file mode 100644 index 2710fa7940e..00000000000 --- a/libraries/ESP32/examples/Touch/TouchButtonV2/ci.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "requires": [ - "CONFIG_SOC_TOUCH_VERSION_2=y" - ] -} diff --git a/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino b/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino index 3b4e5f0b9e9..130026d1ad7 100644 --- a/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino +++ b/libraries/ESP32/examples/Touch/TouchInterrupt/TouchInterrupt.ino @@ -3,11 +3,7 @@ This is an example how to use Touch Intrrerupts The bigger the threshold, the more sensible is the touch */ -#if CONFIG_IDF_TARGET_ESP32P4 -int threshold = 0; // when 0 is used, the benchmarked value will be used -#else -int threshold = 40; -#endif +int threshold = 0; // if 0 is used, benchmark value is used. Its by default 1,5% change, can be changed by touchSetDefaultThreshold(float percentage) bool touch1detected = false; bool touch2detected = false; @@ -23,6 +19,10 @@ void gotTouch2() { void setup() { Serial.begin(115200); delay(1000); // give me time to bring up serial monitor + + //Optional: Set the threshold to 5% of the benchmark value. Only effective if threshold = 0. + touchSetDefaultThreshold(5); + Serial.println("ESP32 Touch Interrupt Test"); touchAttachInterrupt(T2, gotTouch1, threshold); touchAttachInterrupt(T3, gotTouch2, threshold); diff --git a/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino b/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino index 8e93ba44691..ba221f3048f 100644 --- a/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino +++ b/libraries/ESP32/examples/Touch/TouchRead/TouchRead.ino @@ -1,5 +1,5 @@ // ESP32 Touch Test -// Just test touch pin - Touch0 is T0 which is on GPIO 4. +// Just test touch pin - Touch2 is T2 which is on GPIO 2. void setup() { Serial.begin(115200); @@ -8,6 +8,6 @@ void setup() { } void loop() { - Serial.println(touchRead(T1)); // get value using T0 + Serial.println(touchRead(T2)); // get value using T2 delay(1000); } diff --git a/libraries/ESP_I2S/library.properties b/libraries/ESP_I2S/library.properties index b2763f4e7e8..18c48c095a8 100644 --- a/libraries/ESP_I2S/library.properties +++ b/libraries/ESP_I2S/library.properties @@ -4,6 +4,6 @@ author=me-no-dev maintainer=me-no-dev sentence=Library for ESP I2S communication paragraph=Supports ESP32 Arduino platforms. -category=Sound +category=Signal Input/Output url=https://github.com/espressif/arduino-esp32/ architectures=esp32 diff --git a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino index d30d6cd40cf..ec61cb57cf9 100644 --- a/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino +++ b/libraries/ESP_NOW/examples/ESP_NOW_Network/ESP_NOW_Network.ino @@ -32,6 +32,7 @@ #include // For the MAC2STR and MACSTR macros #include +#include //std::nothrow /* Definitions */ @@ -235,7 +236,7 @@ void register_new_peer(const esp_now_recv_info_t *info, const uint8_t *data, int if (current_peer_count < ESPNOW_PEER_COUNT) { Serial.printf("New peer found: " MACSTR " with priority %d\n", MAC2STR(info->src_addr), priority); - ESP_NOW_Network_Peer *new_peer = new ESP_NOW_Network_Peer(info->src_addr, priority); + ESP_NOW_Network_Peer *new_peer = new (std::nothrow) ESP_NOW_Network_Peer(info->src_addr, priority); if (new_peer == nullptr || !new_peer->begin()) { Serial.println("Failed to create or register the new peer"); delete new_peer; diff --git a/libraries/ESP_NOW/library.properties b/libraries/ESP_NOW/library.properties index f8e627dbc03..dd61bf0d4c2 100644 --- a/libraries/ESP_NOW/library.properties +++ b/libraries/ESP_NOW/library.properties @@ -4,6 +4,6 @@ author=me-no-dev maintainer=P-R-O-C-H-Y sentence=Library for ESP_NOW paragraph=Supports ESP32 Arduino platforms. -category=Sensor +category=Communication url=https://github.com/espressif/arduino-esp32/ architectures=esp32 diff --git a/libraries/ESP_NOW/src/ESP32_NOW.h b/libraries/ESP_NOW/src/ESP32_NOW.h index 5b5bbe72673..6bf612a1263 100644 --- a/libraries/ESP_NOW/src/ESP32_NOW.h +++ b/libraries/ESP_NOW/src/ESP32_NOW.h @@ -87,6 +87,8 @@ class ESP_NOW_Peer { friend bool ESP_NOW_Class::removePeer(ESP_NOW_Peer &); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ESP_NOW) extern ESP_NOW_Class ESP_NOW; +#endif #endif diff --git a/libraries/ESP_SR/examples/Basic/Basic.ino b/libraries/ESP_SR/examples/Basic/Basic.ino index 30aab69d79b..c8a16b1bc92 100644 --- a/libraries/ESP_SR/examples/Basic/Basic.ino +++ b/libraries/ESP_SR/examples/Basic/Basic.ino @@ -9,6 +9,17 @@ #define LIGHT_PIN 40 #define FAN_PIN 41 +/** + * The input format: + * M to represent the microphone channel + * R to represent the playback reference channel + * N to represent an unknown or unused channel + * + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ +#define SR_INPUT_FORMAT "MM" + I2SClass i2s; // Generated using the following command: @@ -69,7 +80,7 @@ void setup() { i2s.begin(I2S_MODE_STD, 16000, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); ESP_SR.onEvent(onSrEvent); - ESP_SR.begin(i2s, sr_commands, sizeof(sr_commands) / sizeof(sr_cmd_t), SR_CHANNELS_STEREO, SR_MODE_WAKEWORD); + ESP_SR.begin(i2s, sr_commands, sizeof(sr_commands) / sizeof(sr_cmd_t), SR_CHANNELS_STEREO, SR_MODE_WAKEWORD, SR_INPUT_FORMAT); } void loop() {} diff --git a/libraries/ESP_SR/examples/Basic/ci.json b/libraries/ESP_SR/examples/Basic/ci.json index ec0e969a7d0..ed7699a7857 100644 --- a/libraries/ESP_SR/examples/Basic/ci.json +++ b/libraries/ESP_SR/examples/Basic/ci.json @@ -2,6 +2,9 @@ "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": [ @@ -12,7 +15,6 @@ "esp32c3": false, "esp32c6": false, "esp32h2": false, - "esp32p4": false, "esp32s2": false, "esp32c5": false } diff --git a/libraries/ESP_SR/src/ESP_SR.cpp b/libraries/ESP_SR/src/ESP_SR.cpp index a149c1490e1..31cf85efd37 100644 --- a/libraries/ESP_SR/src/ESP_SR.cpp +++ b/libraries/ESP_SR/src/ESP_SR.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "ESP_SR.h" static esp_err_t on_sr_fill(void *arg, void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms) { @@ -25,9 +25,9 @@ void ESP_SR_Class::onEvent(sr_cb event_cb) { cb = event_cb; } -bool ESP_SR_Class::begin(I2SClass &_i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan, sr_mode_t mode) { +bool ESP_SR_Class::begin(I2SClass &_i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format) { i2s = &_i2s; - esp_err_t err = sr_start(on_sr_fill, this, rx_chan, mode, sr_commands, sr_commands_len, on_sr_event, this); + esp_err_t err = sr_start(on_sr_fill, this, rx_chan, mode, input_format, sr_commands, sr_commands_len, on_sr_event, this); return (err == ESP_OK); } diff --git a/libraries/ESP_SR/src/ESP_SR.h b/libraries/ESP_SR/src/ESP_SR.h index d637f686f42..05393504cb9 100644 --- a/libraries/ESP_SR/src/ESP_SR.h +++ b/libraries/ESP_SR/src/ESP_SR.h @@ -6,7 +6,7 @@ #pragma once #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "ESP_I2S.h" #include "esp32-hal-sr.h" @@ -23,7 +23,19 @@ class ESP_SR_Class { ~ESP_SR_Class(); void onEvent(sr_cb cb); - bool begin(I2SClass &i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan = SR_CHANNELS_STEREO, sr_mode_t mode = SR_MODE_WAKEWORD); + /** + * The input format: + * M to represent the microphone channel + * R to represent the playback reference channel + * N to represent an unknown or unused channel + * + * For example, input_format="MMNR" indicates that the input data consists of four channels, + * which are the microphone channel, the microphone channel, an unused channel, and the playback channel + */ + bool begin( + I2SClass &i2s, const sr_cmd_t *sr_commands, size_t sr_commands_len, sr_channels_t rx_chan = SR_CHANNELS_STEREO, sr_mode_t mode = SR_MODE_WAKEWORD, + const char *input_format = "MN" + ); bool end(void); bool setMode(sr_mode_t mode); bool pause(void); @@ -33,6 +45,8 @@ class ESP_SR_Class { esp_err_t _fill(void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ESP_SR) extern ESP_SR_Class ESP_SR; +#endif #endif // CONFIG_IDF_TARGET_ESP32S3 diff --git a/libraries/ESP_SR/src/esp32-hal-sr.c b/libraries/ESP_SR/src/esp32-hal-sr.c index eb87ef636c1..1ae96d616eb 100644 --- a/libraries/ESP_SR/src/esp32-hal-sr.c +++ b/libraries/ESP_SR/src/esp32-hal-sr.c @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Unlicense OR CC0-1.0 */ #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #if !defined(ARDUINO_PARTITION_esp_sr_32) && !defined(ARDUINO_PARTITION_esp_sr_16) && !defined(ARDUINO_PARTITION_esp_sr_8) #warning Compatible partition must be selected for ESP_SR to work @@ -313,7 +313,8 @@ esp_err_t sr_set_mode(sr_mode_t mode) { } esp_err_t sr_start( - sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t sr_commands[], size_t cmd_number, sr_event_cb cb, void *cb_arg + sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format, const sr_cmd_t sr_commands[], size_t cmd_number, + sr_event_cb cb, void *cb_arg ) { esp_err_t ret = ESP_OK; ESP_RETURN_ON_FALSE(NULL == g_sr_data, ESP_ERR_INVALID_STATE, "SR already running"); @@ -340,12 +341,11 @@ esp_err_t sr_start( models = esp_srmodel_init("model"); // Load WakeWord Detection - g_sr_data->afe_handle = (esp_afe_sr_iface_t *)&ESP_AFE_SR_HANDLE; - afe_config_t afe_config = AFE_CONFIG_DEFAULT(); - afe_config.wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, "hiesp"); - afe_config.aec_init = false; + afe_config_t *afe_config = afe_config_init(input_format, models, AFE_TYPE_SR, AFE_MODE_LOW_COST); + g_sr_data->afe_handle = esp_afe_handle_from_config(afe_config); log_d("load wakenet '%s'", afe_config.wakenet_model_name); - g_sr_data->afe_data = g_sr_data->afe_handle->create_from_config(&afe_config); + g_sr_data->afe_data = g_sr_data->afe_handle->create_from_config(afe_config); + afe_config_free(afe_config); // Load Custom Command Detection char *mn_name = esp_srmodel_filter(models, ESP_MN_PREFIX, ESP_MN_ENGLISH); diff --git a/libraries/ESP_SR/src/esp32-hal-sr.h b/libraries/ESP_SR/src/esp32-hal-sr.h index 894390634e0..ac3df3f07ac 100644 --- a/libraries/ESP_SR/src/esp32-hal-sr.h +++ b/libraries/ESP_SR/src/esp32-hal-sr.h @@ -6,7 +6,7 @@ #pragma once #include "sdkconfig.h" -#if CONFIG_IDF_TARGET_ESP32S3 && (CONFIG_USE_WAKENET || CONFIG_USE_MULTINET) +#if (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4) && (CONFIG_MODEL_IN_FLASH || CONFIG_MODEL_IN_SDCARD) #include "driver/i2s_types.h" #include "esp_err.h" @@ -49,7 +49,8 @@ typedef void (*sr_event_cb)(void *arg, sr_event_t event, int command_id, int phr typedef esp_err_t (*sr_fill_cb)(void *arg, void *out, size_t len, size_t *bytes_read, uint32_t timeout_ms); esp_err_t sr_start( - sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const sr_cmd_t *sr_commands, size_t cmd_number, sr_event_cb cb, void *cb_arg + sr_fill_cb fill_cb, void *fill_cb_arg, sr_channels_t rx_chan, sr_mode_t mode, const char *input_format, const sr_cmd_t *sr_commands, size_t cmd_number, + sr_event_cb cb, void *cb_arg ); esp_err_t sr_stop(void); esp_err_t sr_pause(void); diff --git a/libraries/ESPmDNS/src/ESPmDNS.h b/libraries/ESPmDNS/src/ESPmDNS.h index 1fb90bbf454..74f9002461f 100644 --- a/libraries/ESPmDNS/src/ESPmDNS.h +++ b/libraries/ESPmDNS/src/ESPmDNS.h @@ -127,7 +127,9 @@ 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/src/ETH.cpp b/libraries/Ethernet/src/ETH.cpp index 3dfba37c684..7d808c620d8 100644 --- a/libraries/Ethernet/src/ETH.cpp +++ b/libraries/Ethernet/src/ETH.cpp @@ -34,6 +34,9 @@ #if defined __has_include && __has_include("soc/emac_ext_struct.h") #include "soc/emac_ext_struct.h" #endif /* __has_include("soc/emac_ext_struct.h" */ +#if ETH_PHY_LAN867X_SUPPORTED +#include "esp_eth_phy_lan867x.h" +#endif #include "soc/rtc.h" #endif /* CONFIG_ETH_USE_ESP32_EMAC */ #include "esp32-hal-periman.h" @@ -292,7 +295,10 @@ bool ETHClass::begin(eth_phy_type_t type, int32_t phy_addr, int mdc, int mdio, i case ETH_PHY_DP83848: _phy = esp_eth_phy_new_dp83848(&phy_config); break; case ETH_PHY_KSZ8041: _phy = esp_eth_phy_new_ksz80xx(&phy_config); break; case ETH_PHY_KSZ8081: _phy = esp_eth_phy_new_ksz80xx(&phy_config); break; - default: log_e("Unsupported PHY %d", type); break; +#if ETH_PHY_LAN867X_SUPPORTED + case ETH_PHY_LAN867X: _phy = esp_eth_phy_new_lan867x(&phy_config); break; +#endif + default: log_e("Unsupported PHY %d", type); break; } if (_phy == NULL) { log_e("esp_eth_phy_new failed"); diff --git a/libraries/Ethernet/src/ETH.h b/libraries/Ethernet/src/ETH.h index c52aac6ec6f..f609c651102 100644 --- a/libraries/Ethernet/src/ETH.h +++ b/libraries/Ethernet/src/ETH.h @@ -78,6 +78,9 @@ #include "esp_netif.h" #if CONFIG_ETH_USE_ESP32_EMAC +#if defined __has_include && __has_include("esp_eth_phy_lan867x.h") +#define ETH_PHY_LAN867X_SUPPORTED 1 +#endif #define ETH_PHY_IP101 ETH_PHY_TLK110 #if CONFIG_IDF_TARGET_ESP32 typedef enum { @@ -138,6 +141,9 @@ typedef enum { ETH_PHY_DP83848, ETH_PHY_KSZ8041, ETH_PHY_KSZ8081, +#if ETH_PHY_LAN867X_SUPPORTED + ETH_PHY_LAN867X, +#endif #endif /* CONFIG_ETH_USE_ESP32_EMAC */ #if CONFIG_ETH_SPI_ETHERNET_DM9051 ETH_PHY_DM9051, @@ -268,7 +274,9 @@ class ETHClass : public NetworkInterface { friend class EthernetClass; // to access beginSPI }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ETH) extern ETHClass ETH; +#endif #endif /* _ETH_H_ */ #endif /* CONFIG_ETH_ENABLED */ diff --git a/libraries/FFat/examples/FFat_time/FFat_time.ino b/libraries/FFat/examples/FFat_time/FFat_time.ino index 392647c9923..e379c5f4f4d 100644 --- a/libraries/FFat/examples/FFat_time/FFat_time.ino +++ b/libraries/FFat/examples/FFat_time/FFat_time.ino @@ -28,10 +28,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -42,10 +43,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/FFat/src/FFat.h b/libraries/FFat/src/FFat.h index 3f700396777..56929f2f259 100644 --- a/libraries/FFat/src/FFat.h +++ b/libraries/FFat/src/FFat.h @@ -39,6 +39,8 @@ class F_Fat : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_FFAT) extern fs::F_Fat FFat; +#endif #endif /* _FFAT_H_ */ diff --git a/libraries/FS/src/vfs_api.cpp b/libraries/FS/src/vfs_api.cpp index 616f37ac611..da22ea1962c 100644 --- a/libraries/FS/src/vfs_api.cpp +++ b/libraries/FS/src/vfs_api.cpp @@ -13,6 +13,9 @@ // limitations under the License. #include "vfs_api.h" +#include +#include +#include using namespace fs; @@ -38,67 +41,49 @@ FileImplPtr VFSImpl::open(const char *fpath, const char *mode, const bool create strcpy(temp, _mountpoint); strcat(temp, fpath); - struct stat st; - //file found - if (!stat(temp, &st)) { - free(temp); - if (S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) { - return std::make_shared(this, fpath, mode); - } - log_e("%s has wrong mode 0x%08X", fpath, st.st_mode); - return FileImplPtr(); - } - - //try to open this as directory (might be mount point) - DIR *d = opendir(temp); - if (d) { - closedir(d); - free(temp); - return std::make_shared(this, fpath, mode); - } - - //file not found but mode permits file creation without folder creation - if ((mode && mode[0] != 'r') && (!create)) { - free(temp); - return std::make_shared(this, fpath, mode); - } - - ////file not found but mode permits file creation and folder creation - if ((mode && mode[0] != 'r') && create) { - - char *token; - char *folder = (char *)malloc(strlen(fpath)); - - int start_index = 0; - int end_index = 0; - - token = strchr(fpath + 1, '/'); - end_index = (token - fpath); - - while (token != NULL) { - memcpy(folder, fpath + start_index, end_index - start_index); - folder[end_index - start_index] = '\0'; - - if (!VFSImpl::mkdir(folder)) { - log_e("Creating folder: %s failed!", folder); - return FileImplPtr(); + // Try to open as file first - let the file operation handle errors + if (mode && mode[0] != 'r') { + // For write modes, attempt to create directories if needed + if (create) { + char *token; + char *folder = (char *)malloc(strlen(fpath) + 1); + + int start_index = 0; + int end_index = 0; + + token = strchr(fpath + 1, '/'); + end_index = (token - fpath); + + while (token != NULL) { + memcpy(folder, fpath + start_index, end_index - start_index); + folder[end_index - start_index] = '\0'; + + if (!VFSImpl::mkdir(folder)) { + log_e("Creating folder: %s failed!", folder); + free(folder); + free(temp); + return FileImplPtr(); + } + + token = strchr(token + 1, '/'); + if (token != NULL) { + end_index = (token - fpath); + memset(folder, 0, strlen(folder)); + } } - token = strchr(token + 1, '/'); - if (token != NULL) { - end_index = (token - fpath); - memset(folder, 0, strlen(folder)); - } + free(folder); } - free(folder); + // Try to open the file directly - let fopen handle errors free(temp); return std::make_shared(this, fpath, mode); } - log_e("%s does not exist, no permits for creation", temp); + // For read mode, let the VFSFileImpl constructor handle the file opening + // This avoids the TOCTOU race condition while maintaining proper functionality free(temp); - return FileImplPtr(); + return std::make_shared(this, fpath, mode); } bool VFSImpl::exists(const char *fpath) { @@ -125,10 +110,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) { log_e("bad arguments"); return false; } - if (!exists(pathFrom)) { - log_e("%s does not exists", pathFrom); - return false; - } + size_t mountpointLen = strlen(_mountpoint); char *temp1 = (char *)malloc(strlen(pathFrom) + mountpointLen + 1); if (!temp1) { @@ -148,6 +130,7 @@ bool VFSImpl::rename(const char *pathFrom, const char *pathTo) { strcpy(temp2, _mountpoint); strcat(temp2, pathTo); + // Let rename() handle the error if source doesn't exist auto rc = ::rename(temp1, temp2); free(temp1); free(temp2); @@ -165,16 +148,6 @@ bool VFSImpl::remove(const char *fpath) { return false; } - VFSFileImpl f(this, fpath, "r"); - if (!f || f.isDirectory()) { - if (f) { - f.close(); - } - log_e("%s does not exists or is directory", fpath); - return false; - } - f.close(); - char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1); if (!temp) { log_e("malloc failed"); @@ -184,6 +157,7 @@ bool VFSImpl::remove(const char *fpath) { strcpy(temp, _mountpoint); strcat(temp, fpath); + // Let unlink() handle the error if file doesn't exist auto rc = unlink(temp); free(temp); return rc == 0; @@ -231,16 +205,6 @@ bool VFSImpl::rmdir(const char *fpath) { return false; } - VFSFileImpl f(this, fpath, "r"); - if (!f || !f.isDirectory()) { - if (f) { - f.close(); - } - log_e("%s does not exists or is a file", fpath); - return false; - } - f.close(); - char *temp = (char *)malloc(strlen(fpath) + strlen(_mountpoint) + 1); if (!temp) { log_e("malloc failed"); @@ -250,6 +214,7 @@ bool VFSImpl::rmdir(const char *fpath) { strcpy(temp, _mountpoint); strcat(temp, fpath); + // Let rmdir() handle the error if directory doesn't exist auto rc = ::rmdir(temp); free(temp); return rc == 0; @@ -271,29 +236,30 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs return; } - if (!stat(temp, &_stat)) { - //file found - if (S_ISREG(_stat.st_mode)) { - _isDirectory = false; - _f = fopen(temp, mode); - if (!_f) { - log_e("fopen(%s) failed", temp); - } - if (_f && (_stat.st_blksize == 0)) { - setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); - } - } else if (S_ISDIR(_stat.st_mode)) { - _isDirectory = true; - _d = opendir(temp); - if (!_d) { - log_e("opendir(%s) failed", temp); + // For read mode, check if file exists first to determine type + if (!mode || mode[0] == 'r') { + if (!stat(temp, &_stat)) { + //file found + if (S_ISREG(_stat.st_mode)) { + _isDirectory = false; + _f = fopen(temp, mode); + if (!_f) { + log_e("fopen(%s) failed", temp); + } + if (_f && (_stat.st_blksize == 0)) { + setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); + } + } else if (S_ISDIR(_stat.st_mode)) { + _isDirectory = true; + _d = opendir(temp); + if (!_d) { + log_e("opendir(%s) failed", temp); + } + } else { + log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp); } } else { - log_e("Unknown type 0x%08X for file %s", ((_stat.st_mode) & _IFMT), temp); - } - } else { - //file not found - if (!mode || mode[0] == 'r') { + //file not found //try to open as directory _d = opendir(temp); if (_d) { @@ -302,16 +268,16 @@ VFSFileImpl::VFSFileImpl(VFSImpl *fs, const char *fpath, const char *mode) : _fs _isDirectory = false; //log_w("stat(%s) failed", temp); } - } else { - //lets create this new file - _isDirectory = false; - _f = fopen(temp, mode); - if (!_f) { - log_e("fopen(%s) failed", temp); - } - if (_f && (_stat.st_blksize == 0)) { - setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); - } + } + } else { + //lets create this new file + _isDirectory = false; + _f = fopen(temp, mode); + if (!_f) { + log_e("fopen(%s) failed", temp); + } + if (_f && (_stat.st_blksize == 0)) { + setvbuf(_f, NULL, _IOFBF, DEFAULT_FILE_BUFFER_SIZE); } } free(temp); diff --git a/libraries/HTTPClient/examples/Authorization/Authorization.ino b/libraries/HTTPClient/examples/Authorization/Authorization.ino index 02bf9ae93c7..91ba7c1a1a6 100644 --- a/libraries/HTTPClient/examples/Authorization/Authorization.ino +++ b/libraries/HTTPClient/examples/Authorization/Authorization.ino @@ -12,21 +12,19 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -39,7 +37,7 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure traged server and url http.begin("http://user:password@192.168.1.12/test.html"); @@ -54,22 +52,22 @@ void loop() { http.setAuthorization("dXNlcjpwYXN3b3Jk"); */ - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - USE_SERIAL.println(payload); + Serial.println(payload); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino b/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino index e8f5be62438..a3a1f317d6c 100644 --- a/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino +++ b/libraries/HTTPClient/examples/BasicHttpClient/BasicHttpClient.ino @@ -12,8 +12,6 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; /* @@ -49,15 +47,15 @@ const char* ca = \ void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -70,27 +68,27 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure traged server and url //http.begin("https://www.howsmyssl.com/a/check", ca); //HTTPS http.begin("http://example.com/index.html"); //HTTP - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); // httpCode will be negative on error if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { String payload = http.getString(); - USE_SERIAL.println(payload); + Serial.println(payload); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino index 73e127d1261..5d173da8fc0 100644 --- a/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino +++ b/libraries/HTTPClient/examples/BasicHttpsClient/BasicHttpsClient.ino @@ -59,7 +59,8 @@ void setClock() { struct tm timeinfo; gmtime_r(&nowSecs, &timeinfo); Serial.print(F("Current time: ")); - Serial.print(asctime(&timeinfo)); + char buf[26]; + Serial.print(asctime_r(&timeinfo, buf)); } WiFiMulti WiFiMulti; diff --git a/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino b/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino new file mode 100644 index 00000000000..a73af191fcf --- /dev/null +++ b/libraries/HTTPClient/examples/CustomHeaders/CustomHeaders.ino @@ -0,0 +1,83 @@ +#include + +#include +#include + +// Enable or disable collecting all headers +#define COLLECT_ALL_HEADERS true + +void setup() { + + Serial.begin(115200); + + Serial.println(); + Serial.println(); + Serial.println(); + + for (uint8_t t = 4; t > 0; t--) { + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); + delay(1000); + } + + WiFi.begin("SSID", "PASSWORD"); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(); + Serial.println("Connected to WiFi: " + WiFi.SSID()); +} + +void loop() { + + HTTPClient http; + + Serial.print("[HTTP] Preparing HTTP request...\n"); + // This page will return the headers we want to test + some others + http.begin("https://httpbingo.org/response-headers?x-custom-header=value:42"); + +#if COLLECT_ALL_HEADERS + // Collect all headers + http.collectAllHeaders(); +#else + // Collect specific headers, only that one will be stored + const char *headerKeys[] = {"x-custom-header"}; + const size_t headerKeysCount = sizeof(headerKeys) / sizeof(headerKeys[0]); + http.collectHeaders(headerKeys, headerKeysCount); +#endif + + Serial.print("[HTTP] Sending HTTP GET request...\n"); + // start connection and send HTTP header + int httpCode = http.GET(); + + // httpCode will be negative on error + if (httpCode > 0) { + // HTTP header has been send and Server response header has been handled + Serial.printf("[HTTP] GET response code: %d\n", httpCode); + + Serial.println("[HTTP] Headers collected:"); + for (size_t i = 0; i < http.headers(); i++) { + Serial.printf("[HTTP] - '%s': '%s'\n", http.headerName(i).c_str(), http.header(i).c_str()); + } + + Serial.println("[HTTP] Has header 'x-custom-header'? " + String(http.hasHeader("x-custom-header")) + " (expected true)"); + Serial.printf("[HTTP] x-custom-header: '%s' (expected 'value:42')\n", http.header("x-custom-header").c_str()); + Serial.printf("[HTTP] non-existing-header: '%s' (expected empty string)\n", http.header("non-existing-header").c_str()); + +#if COLLECT_ALL_HEADERS + // Server response with multiple headers, one of them is 'server' + Serial.println("[HTTP] Has header 'server'? " + String(http.hasHeader("server")) + " (expected true)"); + Serial.printf("[HTTP] server: '%s'\n", http.header("server").c_str()); +#endif + + } else { + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + } + + http.end(); + + Serial.println("[HTTP] end connection\n\n"); + delay(5000); +} diff --git a/libraries/HTTPClient/examples/CustomHeaders/ci.json b/libraries/HTTPClient/examples/CustomHeaders/ci.json new file mode 100644 index 00000000000..618e46bd244 --- /dev/null +++ b/libraries/HTTPClient/examples/CustomHeaders/ci.json @@ -0,0 +1,6 @@ +{ + "requires_any": [ + "CONFIG_SOC_WIFI_SUPPORTED=y", + "CONFIG_ESP_WIFI_REMOTE_ENABLED=y" + ] +} diff --git a/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino b/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino index 5b3137a0179..7d7d27332e6 100644 --- a/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino +++ b/libraries/HTTPClient/examples/ReuseConnection/ReuseConnection.ino @@ -12,23 +12,21 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; HTTPClient http; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -47,14 +45,14 @@ void loop() { int httpCode = http.GET(); if (httpCode > 0) { - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { - http.writeToStream(&USE_SERIAL); + http.writeToStream(&Serial); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino b/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino index 187526f6300..db7a9fc2622 100644 --- a/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino +++ b/libraries/HTTPClient/examples/StreamHttpClient/StreamHttpClient.ino @@ -12,21 +12,19 @@ #include -#define USE_SERIAL Serial - WiFiMulti wifiMulti; void setup() { - USE_SERIAL.begin(115200); + Serial.begin(115200); - USE_SERIAL.println(); - USE_SERIAL.println(); - USE_SERIAL.println(); + Serial.println(); + Serial.println(); + Serial.println(); for (uint8_t t = 4; t > 0; t--) { - USE_SERIAL.printf("[SETUP] WAIT %d...\n", t); - USE_SERIAL.flush(); + Serial.printf("[SETUP] WAIT %d...\n", t); + Serial.flush(); delay(1000); } @@ -39,18 +37,18 @@ void loop() { HTTPClient http; - USE_SERIAL.print("[HTTP] begin...\n"); + Serial.print("[HTTP] begin...\n"); // configure server and url http.begin("http://192.168.1.12/test.html"); //http.begin("192.168.1.12", 80, "/test.html"); - USE_SERIAL.print("[HTTP] GET...\n"); + Serial.print("[HTTP] GET...\n"); // start connection and send HTTP header int httpCode = http.GET(); if (httpCode > 0) { // HTTP header has been send and Server response header has been handled - USE_SERIAL.printf("[HTTP] GET... code: %d\n", httpCode); + Serial.printf("[HTTP] GET... code: %d\n", httpCode); // file found at server if (httpCode == HTTP_CODE_OK) { @@ -74,7 +72,7 @@ void loop() { int c = stream->readBytes(buff, ((size > sizeof(buff)) ? sizeof(buff) : size)); // write it to Serial - USE_SERIAL.write(buff, c); + Serial.write(buff, c); if (len > 0) { len -= c; @@ -83,11 +81,11 @@ void loop() { delay(1); } - USE_SERIAL.println(); - USE_SERIAL.print("[HTTP] connection closed or file end.\n"); + Serial.println(); + Serial.print("[HTTP] connection closed or file end.\n"); } } else { - USE_SERIAL.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); + Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str()); } http.end(); diff --git a/libraries/HTTPClient/src/HTTPClient.cpp b/libraries/HTTPClient/src/HTTPClient.cpp index ec812f07201..8061042bd3f 100644 --- a/libraries/HTTPClient/src/HTTPClient.cpp +++ b/libraries/HTTPClient/src/HTTPClient.cpp @@ -90,9 +90,6 @@ HTTPClient::~HTTPClient() { if (_client) { _client->stop(); } - if (_currentHeaders) { - delete[] _currentHeaders; - } if (_tcpDeprecated) { _tcpDeprecated.reset(nullptr); } @@ -564,9 +561,12 @@ int HTTPClient::sendRequest(const char *type, uint8_t *payload, size_t size) { bool redirect = false; uint16_t redirectCount = 0; do { - // wipe out any existing headers from previous request - for (size_t i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].value.length() > 0) { + // wipe out any existing headers from previous request, but preserve the keys if collecting specific headers + if (_collectAllHeaders) { + _currentHeaders.clear(); + } else { + // Only clear values, keep the keys for specific header collection + for (size_t i = 0; i < _currentHeaders.size(); ++i) { _currentHeaders[i].value.clear(); } } @@ -1015,19 +1015,24 @@ void HTTPClient::addHeader(const String &name, const String &value, bool first, } } +void HTTPClient::collectAllHeaders(bool collectAll) { + _collectAllHeaders = collectAll; +} + void HTTPClient::collectHeaders(const char *headerKeys[], const size_t headerKeysCount) { - _headerKeysCount = headerKeysCount; - if (_currentHeaders) { - delete[] _currentHeaders; + if (_collectAllHeaders) { + log_w("collectHeaders is ignored when collectAllHeaders is set"); + return; } - _currentHeaders = new RequestArgument[_headerKeysCount]; - for (size_t i = 0; i < _headerKeysCount; i++) { + _currentHeaders.clear(); + _currentHeaders.resize(headerKeysCount); + for (size_t i = 0; i < headerKeysCount; i++) { _currentHeaders[i].key = headerKeys[i]; } } String HTTPClient::header(const char *name) { - for (size_t i = 0; i < _headerKeysCount; ++i) { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { if (_currentHeaders[i].key.equalsIgnoreCase(name)) { return _currentHeaders[i].value; } @@ -1036,25 +1041,25 @@ String HTTPClient::header(const char *name) { } String HTTPClient::header(size_t i) { - if (i < _headerKeysCount) { + if (i < _currentHeaders.size()) { return _currentHeaders[i].value; } return String(); } String HTTPClient::headerName(size_t i) { - if (i < _headerKeysCount) { + if (i < _currentHeaders.size()) { return _currentHeaders[i].key; } return String(); } int HTTPClient::headers() { - return _headerKeysCount; + return _currentHeaders.size(); } bool HTTPClient::hasHeader(const char *name) { - for (size_t i = 0; i < _headerKeysCount; ++i) { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { if ((_currentHeaders[i].key.equalsIgnoreCase(name)) && (_currentHeaders[i].value.length() > 0)) { return true; } @@ -1238,17 +1243,14 @@ int HTTPClient::handleHeaderResponse() { setCookie(date, headerValue); } - for (size_t i = 0; i < _headerKeysCount; i++) { - if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { - // Uncomment the following lines if you need to add support for multiple headers with the same key: - // if (!_currentHeaders[i].value.isEmpty()) { - // // Existing value, append this one with a comma - // _currentHeaders[i].value += ','; - // _currentHeaders[i].value += headerValue; - // } else { - _currentHeaders[i].value = headerValue; - // } - break; // We found a match, stop looking + if (_collectAllHeaders && headerName.length() > 0) { + _currentHeaders.emplace_back(headerName, headerValue); + } else { + for (size_t i = 0; i < _currentHeaders.size(); ++i) { + if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) { + _currentHeaders[i].value = headerValue; + break; // We found a match, stop looking + } } } } @@ -1593,8 +1595,9 @@ void HTTPClient::setCookie(String date, String headerValue) { // overwrite or delete cookie in/from cookie jar time_t now_local = time(NULL); - time_t now_gmt = mktime(gmtime(&now_local)); - + struct tm tm_gmt; + gmtime_r(&now_local, &tm_gmt); + time_t now_gmt = mktime(&tm_gmt); bool found = false; for (auto c = _cookieJar->begin(); c != _cookieJar->end(); ++c) { @@ -1619,8 +1622,9 @@ void HTTPClient::setCookie(String date, String headerValue) { bool HTTPClient::generateCookieString(String *cookieString) { time_t now_local = time(NULL); - time_t now_gmt = mktime(gmtime(&now_local)); - + struct tm tm_gmt; + gmtime_r(&now_local, &tm_gmt); + time_t now_gmt = mktime(&tm_gmt); *cookieString = ""; bool found = false; diff --git a/libraries/HTTPClient/src/HTTPClient.h b/libraries/HTTPClient/src/HTTPClient.h index 80f6da28599..e07cd937d06 100644 --- a/libraries/HTTPClient/src/HTTPClient.h +++ b/libraries/HTTPClient/src/HTTPClient.h @@ -38,7 +38,7 @@ #include #endif // HTTPCLIENT_NOSECURE -/// Cookie jar support +/// Cookie jar and header support #include #define HTTPCLIENT_DEFAULT_TCP_TIMEOUT (5000) @@ -238,6 +238,7 @@ class HTTPClient { void addHeader(const String &name, const String &value, bool first = false, bool replace = true); /// Response handling + void collectAllHeaders(bool collectAll = true); void collectHeaders(const char *headerKeys[], const size_t headerKeysCount); String header(const char *name); // get request header value by name String header(size_t i); // get request header value by number @@ -294,6 +295,7 @@ class HTTPClient { uint16_t _tcpTimeout = HTTPCLIENT_DEFAULT_TCP_TIMEOUT; bool _useHTTP10 = false; bool _secure = false; + bool _collectAllHeaders = false; String _uri; String _protocol; @@ -304,8 +306,7 @@ class HTTPClient { String _acceptEncoding = "identity;q=1,chunked;q=0.1,*;q=0"; /// Response handling - RequestArgument *_currentHeaders = nullptr; - size_t _headerKeysCount = 0; + std::vector _currentHeaders; int _returnCode = 0; int _size = -1; diff --git a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino index d349c6983c1..c542985d71d 100644 --- a/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino +++ b/libraries/HTTPUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino @@ -32,7 +32,8 @@ void setClock() { struct tm timeinfo; gmtime_r(&now, &timeinfo); Serial.print(F("Current time: ")); - Serial.print(asctime(&timeinfo)); + char buf[26]; + Serial.print(asctime_r(&timeinfo, buf)); } /** diff --git a/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h index 65d8cbaa783..952711bef66 100644 --- a/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h +++ b/libraries/HTTPUpdateServer/src/HTTPUpdateServer.h @@ -1,7 +1,6 @@ #ifndef __HTTP_UPDATE_SERVER_H #define __HTTP_UPDATE_SERVER_H -#include #include #include #include @@ -123,7 +122,7 @@ class HTTPUpdateServer { Serial.printf("Update: %s\n", upload.filename.c_str()); } if (upload.name == "filesystem") { - if (!Update.begin(SPIFFS.totalBytes(), U_SPIFFS)) { //start with max available size + if (!Update.begin(UPDATE_SIZE_UNKNOWN, U_SPIFFS)) { //Instead of SPIFFS.totalBytes(). Fix https://github.com/espressif/arduino-esp32/issues/9967 if (_serial_output) { Update.printError(Serial); } diff --git a/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino b/libraries/Hash/examples/HEX/HEX.ino similarity index 94% rename from libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino rename to libraries/Hash/examples/HEX/HEX.ino index f580a763b54..4ad78db3ad9 100644 --- a/libraries/ESP32/examples/Utilities/HEXBuilder/HEXBuilder.ino +++ b/libraries/Hash/examples/HEX/HEX.ino @@ -1,3 +1,9 @@ +/* + Usage example for the HEXBuilder class. + + This example shows how to convert a HEX string to a binary buffer and vice versa. +*/ + #include void setup() { diff --git a/libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino b/libraries/Hash/examples/MD5/MD5.ino similarity index 100% rename from libraries/ESP32/examples/Utilities/MD5Builder/MD5Builder.ino rename to libraries/Hash/examples/MD5/MD5.ino diff --git a/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino b/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino new file mode 100644 index 00000000000..9a37447add5 --- /dev/null +++ b/libraries/Hash/examples/PBKDF2_HMAC/PBKDF2_HMAC.ino @@ -0,0 +1,178 @@ +/* + Usage example for the PBKDF2_HMACBuilder class. + + This example shows how to use the Hash library to hash data using the PBKDF2_HMACBuilder class. + PBKDF2_HMAC (Password-Based Key Derivation Function 2) is a key derivation function that uses a password and a salt to derive a key. + + The PBKDF2_HMACBuilder class takes for arguments: + - A HashBuilder object to use for the HMAC (SHA1Builder, SHA2Builder, SHA3Builder, etc.) + - A password string (default: empty) + - A salt string (default: empty) + - The number of iterations (default: 1000) +*/ + +#include +#include +#include + +void setup() { + Serial.begin(115200); + Serial.println("\n\nPBKDF2-HMAC Example"); + Serial.println("==================="); + + // Test 1: Basic PBKDF2-HMAC-SHA1 + Serial.println("\n1. PBKDF2-HMAC-SHA1 Test (1 iteration)"); + { + SHA1Builder sha1; + PBKDF2_HMACBuilder pbkdf2(&sha1, "password", "salt", 1); + + pbkdf2.begin(); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println("password"); + Serial.print("Salt: "); + Serial.println("salt"); + Serial.print("Iterations: "); + Serial.println(1); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 + String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 2: PBKDF2-HMAC-SHA1 with more iterations + Serial.println("\n2. PBKDF2-HMAC-SHA1 Test (1000 iterations)"); + { + SHA1Builder sha1; + PBKDF2_HMACBuilder pbkdf2(&sha1); + + const char *password = "password"; + const char *salt = "salt"; + + pbkdf2.begin(); + pbkdf2.setPassword(password); + pbkdf2.setSalt(salt); + pbkdf2.setIterations(1000); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println(password); + Serial.print("Salt: "); + Serial.println(salt); + Serial.print("Iterations: "); + Serial.println(1000); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 6e88be8bad7eae9d9e10aa061224034fed48d03f + String expected = "6e88be8bad7eae9d9e10aa061224034fed48d03f"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 3: PBKDF2-HMAC-SHA256 with different password and salt + Serial.println("\n3. PBKDF2-HMAC-SHA256 Test"); + { + SHA256Builder sha256; + PBKDF2_HMACBuilder pbkdf2(&sha256, "mySecretPassword", "randomSalt123", 100); + + pbkdf2.begin(); + pbkdf2.calculate(); + + Serial.print("Password: "); + Serial.println("mySecretPassword"); + Serial.print("Salt: "); + Serial.println("randomSalt123"); + Serial.print("Iterations: "); + Serial.println(100); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8 + String expected = "4ce309e56a37e0a4b9b84b98ed4a94e6c5cd5926cfd3baca3a6dea8c5d7903e8"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + // Test 4: PBKDF2-HMAC-SHA1 with byte arrays + Serial.println("\n4. PBKDF2-HMAC-SHA1 Test (byte arrays)"); + { + SHA1Builder sha1; // or any other hash algorithm based on HashBuilder + PBKDF2_HMACBuilder pbkdf2(&sha1); + + uint8_t password[] = {0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}; // "password" in bytes + uint8_t salt[] = {0x73, 0x61, 0x6c, 0x74}; // "salt" in bytes + + pbkdf2.begin(); + pbkdf2.setPassword(password, sizeof(password)); + pbkdf2.setSalt(salt, sizeof(salt)); + pbkdf2.setIterations(1); + pbkdf2.calculate(); + + Serial.print("Password (bytes): "); + for (int i = 0; i < sizeof(password); i++) { + Serial.print((char)password[i]); + } + Serial.println(); + Serial.print("Salt (bytes): "); + for (int i = 0; i < sizeof(salt); i++) { + Serial.print((char)salt[i]); + } + Serial.println(); + Serial.print("Iterations: "); + Serial.println(1); + Serial.print("Output (hex): "); + Serial.println(pbkdf2.toString()); + + // Expected: 0c60c80f961f0e71f3a9b524af6012062fe037a6 (same as test 1) + String expected = "0c60c80f961f0e71f3a9b524af6012062fe037a6"; + String result = pbkdf2.toString(); + + if (result.equalsIgnoreCase(expected)) { + Serial.println("✓ PASS: Output matches expected value"); + } else { + Serial.println("✗ FAIL: Output does not match expected value"); + Serial.print("Expected: "); + Serial.println(expected); + Serial.print("Got: "); + Serial.println(result); + } + } + + Serial.println("\nPBKDF2-HMAC tests completed!"); +} + +void loop() { + // Nothing to do in loop +} diff --git a/libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino b/libraries/Hash/examples/SHA1/SHA1.ino similarity index 100% rename from libraries/ESP32/examples/Utilities/SHA1Builder/SHA1Builder.ino rename to libraries/Hash/examples/SHA1/SHA1.ino diff --git a/libraries/Hash/examples/SHA2/SHA2.ino b/libraries/Hash/examples/SHA2/SHA2.ino new file mode 100644 index 00000000000..932ea51565e --- /dev/null +++ b/libraries/Hash/examples/SHA2/SHA2.ino @@ -0,0 +1,95 @@ +/* + Usage example for the SHA2Builder class. + + This example shows how to use the SHA2 library to hash data using the SHA2Builder class. + SHA2 (Secure Hash Algorithm 2) provides different output sizes: SHA-224, SHA-256, SHA-384, and SHA-512. + + Available constructors: + - SHA224Builder(): 224-bit hash output + - SHA256Builder(): 256-bit hash output + - SHA384Builder(): 384-bit hash output + - SHA512Builder(): 512-bit hash output + - SHA2Builder(size_t hash_size): Generic class that can be used to create any SHA2 variant implemented +*/ + +#include + +// Expected hash values for validation +const char *EXPECTED_HELLO_WORLD_SHA256 = "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"; +const char *EXPECTED_HELLO_WORLD_SHA512 = + "2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"; +const char *EXPECTED_TEST_MESSAGE_SHA224 = "155b033d801d4dd59b783d76ac3059053c00b2c28340a5a36a427a76"; +const char *EXPECTED_TEST_MESSAGE_SHA384 = "efd336618cbc96551936e5897e6af391d2480513ff8d4fc744e34462edb3111477d2b889c4d5e80e23b5f9d1b636fbd7"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +void setup() { + Serial.begin(115200); + + Serial.println("\n\n\nStart."); + + // Using SHA2Builder class directly with different hash sizes + { + String test_data = "Hello World"; + Serial.println("Test data: " + test_data); + + // Create SHA-256 (default hash size) + SHA2Builder sha2_256; + sha2_256.begin(); + sha2_256.add(test_data); + sha2_256.calculate(); + String hash_256 = sha2_256.toString(); + validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA256, "SHA-256 validation"); + + // Create SHA-512 + SHA2Builder sha2_512(SHA2_512_HASH_SIZE); + sha2_512.begin(); + sha2_512.add(test_data); + sha2_512.calculate(); + String hash_512 = sha2_512.toString(); + validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA512, "SHA-512 validation"); + } + + // Example using SHA224Builder and SHA384Builder + // There are other constructors for other hash sizes available: + // - SHA224Builder() + // - SHA256Builder() + // - SHA384Builder() + // - SHA512Builder() + // - SHA2Builder(size_t hash_size) + { + String test_data = "Test message"; + Serial.println("Test data: " + test_data); + + // Create SHA-224 using specific constructor + SHA224Builder sha2_224; + sha2_224.begin(); + sha2_224.add(test_data); + sha2_224.calculate(); + String hash_224 = sha2_224.toString(); + validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA224, "SHA224Builder validation"); + + // Create SHA-384 using specific constructor + SHA384Builder sha2_384; + sha2_384.begin(); + sha2_384.add(test_data); + sha2_384.calculate(); + String hash_384 = sha2_384.toString(); + validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA384, "SHA384Builder validation"); + } + + Serial.println("Done."); +} + +void loop() {} diff --git a/libraries/Hash/examples/SHA3/SHA3.ino b/libraries/Hash/examples/SHA3/SHA3.ino new file mode 100644 index 00000000000..258559d07f4 --- /dev/null +++ b/libraries/Hash/examples/SHA3/SHA3.ino @@ -0,0 +1,95 @@ +/* + Usage example for the SHA3Builder class. + + This example shows how to use the SHA3 library to hash data using the SHA3Builder class. + SHA3 (Secure Hash Algorithm 3) provides different output sizes: SHA3-224, SHA3-256, SHA3-384, and SHA3-512. + + Available constructors: + - SHA3_224Builder(): 224-bit hash output + - SHA3_256Builder(): 256-bit hash output + - SHA3_384Builder(): 384-bit hash output + - SHA3_512Builder(): 512-bit hash output + - SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented +*/ + +#include + +// Expected hash values for validation +const char *EXPECTED_HELLO_WORLD_SHA3_256 = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"; +const char *EXPECTED_HELLO_WORLD_SHA3_512 = + "3d58a719c6866b0214f96b0a67b37e51a91e233ce0be126a08f35fdf4c043c6126f40139bfbc338d44eb2a03de9f7bb8eff0ac260b3629811e389a5fbee8a894"; +const char *EXPECTED_TEST_MESSAGE_SHA3_224 = "27af391bcb3b86f21b73c42c4abbde4791c395dc650243eede85de0c"; +const char *EXPECTED_TEST_MESSAGE_SHA3_384 = "adb18f6b164672c566950bfefa48c5a851d48ee184f249a19e723d753b7536fcd048c3443aff7ebe433fce63c81726ea"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +void setup() { + Serial.begin(115200); + + Serial.println("\n\n\nStart."); + + // Using SHA3Builder class directly with different hash sizes + { + String test_data = "Hello World"; + Serial.println("Test data: " + test_data); + + // Create SHA3-256 (default hash size) + SHA3Builder sha3_256; + sha3_256.begin(); + sha3_256.add(test_data); + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_HELLO_WORLD_SHA3_256, "SHA3-256 validation"); + + // Create SHA3-512 + SHA3Builder sha3_512(SHA3_512_HASH_SIZE); + sha3_512.begin(); + sha3_512.add(test_data); + sha3_512.calculate(); + String hash_512 = sha3_512.toString(); + validateHash(hash_512, EXPECTED_HELLO_WORLD_SHA3_512, "SHA3-512 validation"); + } + + // Example using SHA3_224Builder and SHA3_384Builder + // There are other constructors for other hash sizes available: + // - SHA3_224Builder() + // - SHA3_256Builder() + // - SHA3_384Builder() + // - SHA3_512Builder() + // - SHA3Builder(size_t hash_size) + { + String test_data = "Test message"; + Serial.println("Test data: " + test_data); + + // Create SHA3-224 using specific constructor + SHA3_224Builder sha3_224; + sha3_224.begin(); + sha3_224.add(test_data); + sha3_224.calculate(); + String hash_224 = sha3_224.toString(); + validateHash(hash_224, EXPECTED_TEST_MESSAGE_SHA3_224, "SHA3_224Builder validation"); + + // Create SHA3-384 using specific constructor + SHA3_384Builder sha3_384; + sha3_384.begin(); + sha3_384.add(test_data); + sha3_384.calculate(); + String hash_384 = sha3_384.toString(); + validateHash(hash_384, EXPECTED_TEST_MESSAGE_SHA3_384, "SHA3_384Builder validation"); + } + + Serial.println("Done."); +} + +void loop() {} diff --git a/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino b/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino new file mode 100644 index 00000000000..ba396b9bc1f --- /dev/null +++ b/libraries/Hash/examples/SHA3Stream/SHA3Stream.ino @@ -0,0 +1,166 @@ +/* + Usage example for the SHA3Builder class with streams. + + This example shows how to use the SHA3 library to hash data from streams using the addStream method. + This is useful for hashing large files or data that comes from various stream sources like: + - File streams + - Network streams + - Memory streams + - Custom stream implementations + + Available constructors: + - SHA3_224Builder(): 224-bit hash output + - SHA3_256Builder(): 256-bit hash output + - SHA3_384Builder(): 384-bit hash output + - SHA3_512Builder(): 512-bit hash output + - SHA3Builder(size_t hash_size): Generic class that can be used to create any SHA3 variant implemented +*/ + +#include +#include + +// Expected hash values for validation +const char *EXPECTED_STREAM_TEST_SHA3_256 = "7094efc774885c7a785b408c5da86636cb8adc79156c0f162c6fd7e49f4c505e"; +const char *EXPECTED_MAX_SHA3_224_FULL = "ad0e69e04a7258d7cab4272a08ac69f8b43f4e45f9c49c9abb0628af"; +const char *EXPECTED_MAX_SHA3_224_10 = "9b55096e998cda6b96d3f2828c4ccda8c9964a1ad98989fb8b0fcd26"; +const char *EXPECTED_COMBINED_SHA3_256 = "4a32307fe03bf9f600c5d124419985fd4d42c1639e6a23ab044f107c3b95a189"; + +// Validation function +bool validateHash(const String &calculated, const char *expected, const String &test_name) { + bool passed = (calculated == expected); + Serial.print(test_name); + Serial.print(": "); + Serial.println(passed ? "PASS" : "FAIL"); + Serial.print(" Expected: "); + Serial.println(expected); + Serial.print(" Got: "); + Serial.println(calculated); + return passed; +} + +// Custom stream class for demonstration +class TestStream : public Stream { +private: + String data; + size_t position; + +public: + TestStream(String input_data) : data(input_data), position(0) {} + + virtual int available() override { + return data.length() - position; + } + + virtual int read() override { + if (position < data.length()) { + return data.charAt(position++); + } + return -1; + } + + virtual int peek() override { + if (position < data.length()) { + return data.charAt(position); + } + return -1; + } + + virtual size_t write(uint8_t) override { + return 0; // Read-only stream + } + + size_t length() { + return data.length(); + } + + void reset() { + position = 0; + } +}; + +void setup() { + Serial.begin(115200); + while (!Serial) { + delay(10); + } + + Serial.println("\n\nSHA3 Stream Example"); + Serial.println("==================="); + + // Example 1: Using addStream with a custom stream + { + Serial.println("\n1. Hashing data from a custom stream:"); + + const char *test_data = "This is a test message for streaming hash calculation. " + "It contains multiple sentences to demonstrate how the " + "addStream method processes data in chunks."; + + TestStream stream(test_data); + + SHA3_256Builder sha3_256; + sha3_256.begin(); + + // Hash the entire stream + // First argument is the stream, second argument is the maximum length to be read from the stream + sha3_256.addStream(stream, stream.length()); // Reading the entire stream + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_STREAM_TEST_SHA3_256, "Stream test validation"); + } + + // Example 2: Using addStream with different maximum lengths + { + Serial.println("\n2. Comparing different maximum lengths with streams:"); + + const char *test_data = "Streaming hash test with different maximum lengths"; + TestStream stream(test_data); + + // SHA3-224 with a hardcoded maximum length + stream.reset(); + SHA3_224Builder sha3_224_10; + sha3_224_10.begin(); + sha3_224_10.addStream(stream, 10); // Passing a hardcoded maximum length to be read from the stream + sha3_224_10.calculate(); + String hash_224_10 = sha3_224_10.toString(); + validateHash(hash_224_10, EXPECTED_MAX_SHA3_224_10, "SHA3-224 with 10 bytes"); + + // SHA3-224 with the full stream + stream.reset(); + SHA3_224Builder sha3_224_full; + sha3_224_full.begin(); + sha3_224_full.addStream(stream, stream.length()); // Reading the entire stream + sha3_224_full.calculate(); + String hash_224_full = sha3_224_full.toString(); + validateHash(hash_224_full, EXPECTED_MAX_SHA3_224_FULL, "SHA3-224 with full stream"); + } + + // Example 3: Combining add() and addStream() + { + Serial.println("\n3. Combining add() and addStream():"); + + const char *stream_data = "Additional data from stream"; + TestStream stream(stream_data); + + SHA3_256Builder sha3_256; + sha3_256.begin(); + + // Add some data directly + sha3_256.add("Initial data: "); + + // Add data from stream + sha3_256.addStream(stream, stream.length()); + + // Add more data directly + sha3_256.add(" : Final data"); + + sha3_256.calculate(); + String hash_256 = sha3_256.toString(); + validateHash(hash_256, EXPECTED_COMBINED_SHA3_256, "Combined data validation"); + } + + Serial.println("\nStream example completed!"); +} + +void loop() { + // Nothing to do in loop +} diff --git a/libraries/Hash/keywords.txt b/libraries/Hash/keywords.txt new file mode 100644 index 00000000000..d553b7b428e --- /dev/null +++ b/libraries/Hash/keywords.txt @@ -0,0 +1,51 @@ +####################################### +# Syntax Coloring Map For Hash +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +HashBuilder KEYWORD1 +HEXBuilder KEYWORD1 +MD5Builder KEYWORD1 +SHA1Builder KEYWORD1 +SHA2Builder KEYWORD1 +SHA224Builder KEYWORD1 +SHA256Builder KEYWORD1 +SHA384Builder KEYWORD1 +SHA512Builder KEYWORD1 +SHA3Builder KEYWORD1 +PBKDF2_HMACBuilder KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +add KEYWORD2 +addHexString KEYWORD2 +addStream KEYWORD2 +calculate KEYWORD2 +getBytes KEYWORD2 +getChars KEYWORD2 +toString KEYWORD2 +hex2bytes KEYWORD2 +bytes2hex KEYWORD2 +getHashSize KEYWORD2 +setPassword KEYWORD2 +setSalt KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + +SHA1_HASH_SIZE LITERAL1 +SHA2_224_HASH_SIZE LITERAL1 +SHA2_256_HASH_SIZE LITERAL1 +SHA2_384_HASH_SIZE LITERAL1 +SHA2_512_HASH_SIZE LITERAL1 +SHA3_224_HASH_SIZE LITERAL1 +SHA3_256_HASH_SIZE LITERAL1 +SHA3_384_HASH_SIZE LITERAL1 +SHA3_512_HASH_SIZE LITERAL1 diff --git a/libraries/Hash/library.properties b/libraries/Hash/library.properties new file mode 100644 index 00000000000..5db4df17c2d --- /dev/null +++ b/libraries/Hash/library.properties @@ -0,0 +1,9 @@ +name=Hash +version=3.3.0 +author=lucasssvaz +maintainer=lucasssvaz +sentence=Bundle of hashing functions for the ESP32 +paragraph=This library provides a set of hashing functions to be used in the sketches +category=Security +url= +architectures=esp32 diff --git a/libraries/Hash/src/PBKDF2_HMACBuilder.cpp b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp new file mode 100644 index 00000000000..125d4bcb061 --- /dev/null +++ b/libraries/Hash/src/PBKDF2_HMACBuilder.cpp @@ -0,0 +1,256 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "PBKDF2_HMACBuilder.h" + +// Block size for HMAC (64 bytes for SHA-1, SHA-256, SHA-512) +#define HMAC_BLOCK_SIZE 64 + +PBKDF2_HMACBuilder::PBKDF2_HMACBuilder(HashBuilder *hash, String password, String salt, uint32_t iterations) { + this->hashBuilder = hash; + this->hashSize = hashBuilder->getHashSize(); + this->iterations = iterations; + + // Initialize pointers + this->password = nullptr; + this->salt = nullptr; + this->passwordLen = 0; + this->saltLen = 0; + this->derivedKey = nullptr; + this->derivedKeyLen = 0; + this->calculated = false; + + if (password.length() > 0) { + setPassword(password); + } + + if (salt.length() > 0) { + setSalt(salt); + } +} + +PBKDF2_HMACBuilder::~PBKDF2_HMACBuilder() { + clearData(); +} + +void PBKDF2_HMACBuilder::clearData() { + if (derivedKey != nullptr) { + forced_memzero(derivedKey, derivedKeyLen); + delete[] derivedKey; + derivedKey = nullptr; + } + derivedKeyLen = 0; + calculated = false; +} + +void PBKDF2_HMACBuilder::hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output) { + uint8_t keyPad[HMAC_BLOCK_SIZE]; + uint8_t outerPad[HMAC_BLOCK_SIZE]; + uint8_t innerHash[64]; // Large enough for any hash + + // Prepare key + if (keyLen > HMAC_BLOCK_SIZE) { + // Key is longer than block size, hash it + hashBuilder->begin(); + hashBuilder->add(key, keyLen); + hashBuilder->calculate(); + hashBuilder->getBytes(keyPad); + keyLen = hashSize; + } else { + // Copy key to keyPad + memcpy(keyPad, key, keyLen); + } + + // Pad key with zeros if necessary + if (keyLen < HMAC_BLOCK_SIZE) { + memset(keyPad + keyLen, 0, HMAC_BLOCK_SIZE - keyLen); + } + + // Create outer and inner pads + for (int i = 0; i < HMAC_BLOCK_SIZE; i++) { + outerPad[i] = keyPad[i] ^ 0x5c; + keyPad[i] = keyPad[i] ^ 0x36; + } + + // Inner hash: H(K XOR ipad, text) + hashBuilder->begin(); + hashBuilder->add(keyPad, HMAC_BLOCK_SIZE); + hashBuilder->add(data, dataLen); + hashBuilder->calculate(); + hashBuilder->getBytes(innerHash); + + // Outer hash: H(K XOR opad, inner_hash) + hashBuilder->begin(); + hashBuilder->add(outerPad, HMAC_BLOCK_SIZE); + hashBuilder->add(innerHash, hashSize); + hashBuilder->calculate(); + hashBuilder->getBytes(output); +} + +// HashBuilder interface methods +void PBKDF2_HMACBuilder::begin() { + clearData(); +} + +void PBKDF2_HMACBuilder::add(const uint8_t *data, size_t len) { + log_w("PBKDF2_HMACBuilder::add sets only the password. Use setPassword() and setSalt() instead."); + setPassword(data, len); +} + +bool PBKDF2_HMACBuilder::addStream(Stream &stream, const size_t maxLen) { + log_e("PBKDF2_HMACBuilder does not support addStream. Use setPassword() and setSalt() instead."); + return false; +} + +void PBKDF2_HMACBuilder::calculate() { + if (password == nullptr || salt == nullptr) { + log_e("Error: Password or salt not set."); + return; + } + + // Set default output size to hash size if not specified + if (derivedKeyLen == 0) { + derivedKeyLen = hashSize; + } + + // Allocate output buffer + if (derivedKey != nullptr) { + forced_memzero(derivedKey, derivedKeyLen); + delete[] derivedKey; + } + derivedKey = new uint8_t[derivedKeyLen]; + + // Perform PBKDF2-HMAC + pbkdf2_hmac(password, passwordLen, salt, saltLen, iterations, derivedKey, derivedKeyLen); + calculated = true; +} + +void PBKDF2_HMACBuilder::getBytes(uint8_t *output) { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return; + } + memcpy(output, derivedKey, derivedKeyLen); +} + +void PBKDF2_HMACBuilder::getChars(char *output) { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, derivedKeyLen * 2 + 1, derivedKey, derivedKeyLen); +} + +String PBKDF2_HMACBuilder::toString() { + if (!calculated || derivedKey == nullptr) { + log_e("Error: PBKDF2-HMAC not calculated or no output buffer provided."); + return ""; + } + + char out[(derivedKeyLen * 2) + 1]; + getChars(out); + return String(out); +} + +// PBKDF2 specific methods +void PBKDF2_HMACBuilder::setPassword(const uint8_t *password, size_t len) { + if (this->password != nullptr) { + forced_memzero(this->password, len); + delete[] this->password; + } + this->password = new uint8_t[len]; + memcpy(this->password, password, len); + this->passwordLen = len; + calculated = false; +} + +void PBKDF2_HMACBuilder::setPassword(const char *password) { + setPassword((const uint8_t *)password, strlen(password)); +} + +void PBKDF2_HMACBuilder::setPassword(String password) { + setPassword((const uint8_t *)password.c_str(), password.length()); +} + +void PBKDF2_HMACBuilder::setSalt(const uint8_t *salt, size_t len) { + if (this->salt != nullptr) { + forced_memzero(this->salt, len); + delete[] this->salt; + } + this->salt = new uint8_t[len]; + memcpy(this->salt, salt, len); + this->saltLen = len; + calculated = false; +} + +void PBKDF2_HMACBuilder::setSalt(const char *salt) { + setSalt((const uint8_t *)salt, strlen(salt)); +} + +void PBKDF2_HMACBuilder::setSalt(String salt) { + setSalt((const uint8_t *)salt.c_str(), salt.length()); +} + +void PBKDF2_HMACBuilder::setIterations(uint32_t iterations) { + this->iterations = iterations; +} + +void PBKDF2_HMACBuilder::setHashAlgorithm(HashBuilder *hash) { + // Set the hash algorithm to use for the HMAC + // Note: We don't delete hashBuilder here as it might be owned by the caller + // The caller is responsible for managing the hashBuilder lifetime + hashBuilder = hash; + hashSize = hashBuilder->getHashSize(); +} + +void PBKDF2_HMACBuilder::pbkdf2_hmac( + const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen +) { + uint8_t u1[64]; // Large enough for any hash + uint8_t u2[64]; + uint8_t saltWithBlock[256]; // Salt + block number + uint8_t block[64]; + + size_t blocks = (outputLen + hashSize - 1) / hashSize; + + for (size_t i = 1; i <= blocks; i++) { + // Prepare salt || INT(i) + memcpy(saltWithBlock, salt, saltLen); + saltWithBlock[saltLen] = (i >> 24) & 0xFF; + saltWithBlock[saltLen + 1] = (i >> 16) & 0xFF; + saltWithBlock[saltLen + 2] = (i >> 8) & 0xFF; + saltWithBlock[saltLen + 3] = i & 0xFF; + + // U1 = HMAC(password, salt || INT(i)) + hmac(password, passwordLen, saltWithBlock, saltLen + 4, u1); + memcpy(block, u1, hashSize); + + // U2 = HMAC(password, U1) + for (uint32_t j = 1; j < iterations; j++) { + hmac(password, passwordLen, u1, hashSize, u2); + memcpy(u1, u2, hashSize); + + // XOR with previous result + for (size_t k = 0; k < hashSize; k++) { + block[k] ^= u1[k]; + } + } + + // Copy block to output + size_t copyLen = (i == blocks) ? (outputLen - (i - 1) * hashSize) : hashSize; + memcpy(output + (i - 1) * hashSize, block, copyLen); + } +} diff --git a/libraries/Hash/src/PBKDF2_HMACBuilder.h b/libraries/Hash/src/PBKDF2_HMACBuilder.h new file mode 100644 index 00000000000..2a4a477c1c5 --- /dev/null +++ b/libraries/Hash/src/PBKDF2_HMACBuilder.h @@ -0,0 +1,73 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PBKDF2_HMACBuilder_h +#define PBKDF2_HMACBuilder_h + +#include +#include +#include "HashBuilder.h" + +class PBKDF2_HMACBuilder : public HashBuilder { +private: + HashBuilder *hashBuilder; + size_t hashSize; + uint32_t iterations; + + // Password and salt storage + uint8_t *password; + size_t passwordLen; + uint8_t *salt; + size_t saltLen; + + // Output storage + uint8_t *derivedKey; + size_t derivedKeyLen; + bool calculated; + + void hmac(const uint8_t *key, size_t keyLen, const uint8_t *data, size_t dataLen, uint8_t *output); + void pbkdf2_hmac(const uint8_t *password, size_t passwordLen, const uint8_t *salt, size_t saltLen, uint32_t iterations, uint8_t *output, size_t outputLen); + void clearData(); + +public: + using HashBuilder::add; + + // Constructor takes a hash builder instance + PBKDF2_HMACBuilder(HashBuilder *hash, String password = "", String salt = "", uint32_t iterations = 10000); + ~PBKDF2_HMACBuilder(); + + // Standard HashBuilder interface + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + size_t getHashSize() const override { + return derivedKeyLen; + } + + // PBKDF2 specific methods + void setPassword(const uint8_t *password, size_t len); + void setPassword(const char *password); + void setPassword(String password); + void setSalt(const uint8_t *salt, size_t len); + void setSalt(const char *salt); + void setSalt(String salt); + void setIterations(uint32_t iterations); + void setHashAlgorithm(HashBuilder *hash); +}; + +#endif diff --git a/cores/esp32/SHA1Builder.cpp b/libraries/Hash/src/SHA1Builder.cpp similarity index 88% rename from cores/esp32/SHA1Builder.cpp rename to libraries/Hash/src/SHA1Builder.cpp index 6bbe3ca83e0..ab9e6550513 100644 --- a/cores/esp32/SHA1Builder.cpp +++ b/libraries/Hash/src/SHA1Builder.cpp @@ -1,27 +1,21 @@ -/* - * FIPS-180-1 compliant SHA-1 implementation - * - * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * This file is part of mbed TLS (https://tls.mbed.org) - * Modified for esp32 by Lucas Saavedra Vaz on 11 Jan 2024 - */ +// Copyright 2024 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Based on mbed TLS (https://tls.mbed.org) #include -#include +#include "SHA1Builder.h" // 32-bit integer manipulation macros (big endian) @@ -201,6 +195,8 @@ void SHA1Builder::process(const uint8_t *data) { // Public methods void SHA1Builder::begin(void) { + finalized = false; + total[0] = 0; total[1] = 0; @@ -218,7 +214,7 @@ void SHA1Builder::add(const uint8_t *data, size_t len) { size_t fill; uint32_t left; - if (len == 0) { + if (finalized || len == 0) { return; } @@ -251,17 +247,6 @@ void SHA1Builder::add(const uint8_t *data, size_t len) { } } -void SHA1Builder::addHexString(const char *data) { - uint16_t len = strlen(data); - uint8_t *tmp = (uint8_t *)malloc(len / 2); - if (tmp == NULL) { - return; - } - hex2bytes(tmp, len / 2, data); - add(tmp, len / 2); - free(tmp); -} - bool SHA1Builder::addStream(Stream &stream, const size_t maxLen) { const int buf_size = 512; int maxLengthLeft = maxLen; @@ -306,6 +291,10 @@ void SHA1Builder::calculate(void) { uint32_t high, low; uint8_t msglen[8]; + if (finalized) { + return; + } + high = (total[0] >> 29) | (total[1] << 3); low = (total[0] << 3); @@ -323,6 +312,8 @@ void SHA1Builder::calculate(void) { PUT_UINT32_BE(state[2], hash, 8); PUT_UINT32_BE(state[3], hash, 12); PUT_UINT32_BE(state[4], hash, 16); + + finalized = true; } void SHA1Builder::getBytes(uint8_t *output) { @@ -330,6 +321,11 @@ void SHA1Builder::getBytes(uint8_t *output) { } void SHA1Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA1 not calculated or no output buffer provided."); + return; + } + bytes2hex(output, SHA1_HASH_SIZE * 2 + 1, hash, SHA1_HASH_SIZE); } diff --git a/cores/esp32/SHA1Builder.h b/libraries/Hash/src/SHA1Builder.h similarity index 88% rename from cores/esp32/SHA1Builder.h rename to libraries/Hash/src/SHA1Builder.h index b587e4fdc96..3cbf8aad96a 100644 --- a/cores/esp32/SHA1Builder.h +++ b/libraries/Hash/src/SHA1Builder.h @@ -28,23 +28,24 @@ class SHA1Builder : public HashBuilder { uint32_t state[5]; /* intermediate digest state */ unsigned char buffer[64]; /* data block being processed */ uint8_t hash[SHA1_HASH_SIZE]; /* SHA-1 result */ + bool finalized; /* Whether hash has been finalized */ void process(const uint8_t *data); public: - void begin() override; - using HashBuilder::add; - void add(const uint8_t *data, size_t len) override; - - using HashBuilder::addHexString; - void addHexString(const char *data) override; + SHA1Builder() : finalized(false) {} + void begin() override; + void add(const uint8_t *data, size_t len) override; bool addStream(Stream &stream, const size_t maxLen) override; void calculate() override; void getBytes(uint8_t *output) override; void getChars(char *output) override; String toString() override; + size_t getHashSize() const override { + return SHA1_HASH_SIZE; + } }; #endif diff --git a/libraries/Hash/src/SHA2Builder.cpp b/libraries/Hash/src/SHA2Builder.cpp new file mode 100644 index 00000000000..878f0ef7c8b --- /dev/null +++ b/libraries/Hash/src/SHA2Builder.cpp @@ -0,0 +1,421 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "esp32-hal-log.h" +#include "SHA2Builder.h" + +// SHA-256 constants +static const uint32_t sha256_k[64] = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, + 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, + 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, + 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +// SHA-512 constants +static const uint64_t sha512_k[80] = {0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, + 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, + 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, + 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, + 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, + 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL}; + +// Macros for bit manipulation +#define ROTR32(x, n) (((x) >> (n)) | ((x) << (32 - (n)))) +#define ROTR64(x, n) (((x) >> (n)) | ((x) << (64 - (n)))) +#define CH32(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define CH64(x, y, z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ32(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define MAJ64(x, y, z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0_32(x) (ROTR32(x, 2) ^ ROTR32(x, 13) ^ ROTR32(x, 22)) +#define EP0_64(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define EP1_32(x) (ROTR32(x, 6) ^ ROTR32(x, 11) ^ ROTR32(x, 25)) +#define EP1_64(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SIG0_32(x) (ROTR32(x, 7) ^ ROTR32(x, 18) ^ ((x) >> 3)) +#define SIG0_64(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ ((x) >> 7)) +#define SIG1_32(x) (ROTR32(x, 17) ^ ROTR32(x, 19) ^ ((x) >> 10)) +#define SIG1_64(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ ((x) >> 6)) + +// Byte order conversion +#define BYTESWAP32(x) ((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)) +#define BYTESWAP64(x) (((uint64_t)BYTESWAP32((uint32_t)((x) >> 32))) | (((uint64_t)BYTESWAP32((uint32_t)(x))) << 32)) + +// Constructor +SHA2Builder::SHA2Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false), total_length(0) { + // Determine block size and algorithm family + if (hash_size == SHA2_224_HASH_SIZE || hash_size == SHA2_256_HASH_SIZE) { + block_size = SHA2_256_BLOCK_SIZE; + is_sha512 = false; + } else if (hash_size == SHA2_384_HASH_SIZE || hash_size == SHA2_512_HASH_SIZE) { + block_size = SHA2_512_BLOCK_SIZE; + is_sha512 = true; + } else { + log_e("Invalid hash size: %d", hash_size); + block_size = 0; + is_sha512 = false; + } +} + +// Initialize the hash computation +void SHA2Builder::begin() { + // Clear the state and buffer + memset(state_32, 0, sizeof(state_32)); + memset(state_64, 0, sizeof(state_64)); + memset(buffer, 0, sizeof(buffer)); + buffer_size = 0; + finalized = false; + total_length = 0; + + // Initialize state based on algorithm + if (!is_sha512) { + // SHA-224/256 initial values + if (hash_size == SHA2_224_HASH_SIZE) { + // SHA-224 initial values + state_32[0] = 0xc1059ed8; + state_32[1] = 0x367cd507; + state_32[2] = 0x3070dd17; + state_32[3] = 0xf70e5939; + state_32[4] = 0xffc00b31; + state_32[5] = 0x68581511; + state_32[6] = 0x64f98fa7; + state_32[7] = 0xbefa4fa4; + } else { + // SHA-256 initial values + state_32[0] = 0x6a09e667; + state_32[1] = 0xbb67ae85; + state_32[2] = 0x3c6ef372; + state_32[3] = 0xa54ff53a; + state_32[4] = 0x510e527f; + state_32[5] = 0x9b05688c; + state_32[6] = 0x1f83d9ab; + state_32[7] = 0x5be0cd19; + } + } else { + // SHA-384/512 initial values + if (hash_size == SHA2_384_HASH_SIZE) { + // SHA-384 initial values + state_64[0] = 0xcbbb9d5dc1059ed8ULL; + state_64[1] = 0x629a292a367cd507ULL; + state_64[2] = 0x9159015a3070dd17ULL; + state_64[3] = 0x152fecd8f70e5939ULL; + state_64[4] = 0x67332667ffc00b31ULL; + state_64[5] = 0x8eb44a8768581511ULL; + state_64[6] = 0xdb0c2e0d64f98fa7ULL; + state_64[7] = 0x47b5481dbefa4fa4ULL; + } else { + // SHA-512 initial values + state_64[0] = 0x6a09e667f3bcc908ULL; + state_64[1] = 0xbb67ae8584caa73bULL; + state_64[2] = 0x3c6ef372fe94f82bULL; + state_64[3] = 0xa54ff53a5f1d36f1ULL; + state_64[4] = 0x510e527fade682d1ULL; + state_64[5] = 0x9b05688c2b3e6c1fULL; + state_64[6] = 0x1f83d9abfb41bd6bULL; + state_64[7] = 0x5be0cd19137e2179ULL; + } + } +} + +// Process a block for SHA-256 +void SHA2Builder::process_block_sha256(const uint8_t *data) { + uint32_t w[64]; + uint32_t a, b, c, d, e, f, g, h; + uint32_t t1, t2; + + // Prepare message schedule + for (int i = 0; i < 16; i++) { + w[i] = BYTESWAP32(((uint32_t *)data)[i]); + } + for (int i = 16; i < 64; i++) { + w[i] = SIG1_32(w[i - 2]) + w[i - 7] + SIG0_32(w[i - 15]) + w[i - 16]; + } + + // Initialize working variables + a = state_32[0]; + b = state_32[1]; + c = state_32[2]; + d = state_32[3]; + e = state_32[4]; + f = state_32[5]; + g = state_32[6]; + h = state_32[7]; + + // Main loop + for (int i = 0; i < 64; i++) { + t1 = h + EP1_32(e) + CH32(e, f, g) + sha256_k[i] + w[i]; + t2 = EP0_32(a) + MAJ32(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Add the compressed chunk to the current hash value + state_32[0] += a; + state_32[1] += b; + state_32[2] += c; + state_32[3] += d; + state_32[4] += e; + state_32[5] += f; + state_32[6] += g; + state_32[7] += h; +} + +// Process a block for SHA-512 +void SHA2Builder::process_block_sha512(const uint8_t *data) { + uint64_t w[80]; + uint64_t a, b, c, d, e, f, g, h; + uint64_t t1, t2; + + // Prepare message schedule + for (int i = 0; i < 16; i++) { + w[i] = BYTESWAP64(((uint64_t *)data)[i]); + } + for (int i = 16; i < 80; i++) { + w[i] = SIG1_64(w[i - 2]) + w[i - 7] + SIG0_64(w[i - 15]) + w[i - 16]; + } + + // Initialize working variables + a = state_64[0]; + b = state_64[1]; + c = state_64[2]; + d = state_64[3]; + e = state_64[4]; + f = state_64[5]; + g = state_64[6]; + h = state_64[7]; + + // Main loop + for (int i = 0; i < 80; i++) { + t1 = h + EP1_64(e) + CH64(e, f, g) + sha512_k[i] + w[i]; + t2 = EP0_64(a) + MAJ64(a, b, c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + // Add the compressed chunk to the current hash value + state_64[0] += a; + state_64[1] += b; + state_64[2] += c; + state_64[3] += d; + state_64[4] += e; + state_64[5] += f; + state_64[6] += g; + state_64[7] += h; +} + +// Add data to the hash computation +void SHA2Builder::add(const uint8_t *data, size_t len) { + if (finalized || len == 0) { + return; + } + + total_length += len; + size_t offset = 0; + + // Process any buffered data first + if (buffer_size > 0) { + size_t to_copy = std::min(len, block_size - buffer_size); + memcpy(buffer + buffer_size, data, to_copy); + buffer_size += to_copy; + offset += to_copy; + + if (buffer_size == block_size) { + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + buffer_size = 0; + } + } + + // Process full blocks + while (offset + block_size <= len) { + if (is_sha512) { + process_block_sha512(data + offset); + } else { + process_block_sha256(data + offset); + } + offset += block_size; + } + + // Buffer remaining data + if (offset < len) { + memcpy(buffer, data + offset, len - offset); + buffer_size = len - offset; + } +} + +// Add data from a stream +bool SHA2Builder::addStream(Stream &stream, const size_t maxLen) { + const int buf_size = 512; + int maxLengthLeft = maxLen; + uint8_t *buf = (uint8_t *)malloc(buf_size); + + if (!buf) { + return false; + } + + int bytesAvailable = stream.available(); + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) { + // Determine number of bytes to read + int readBytes = bytesAvailable; + if (readBytes > maxLengthLeft) { + readBytes = maxLengthLeft; + } + if (readBytes > buf_size) { + readBytes = buf_size; + } + + // Read data and check if we got something + int numBytesRead = stream.readBytes(buf, readBytes); + if (numBytesRead < 1) { + free(buf); + return false; + } + + // Update SHA2 with buffer payload + add(buf, numBytesRead); + + // Update available number of bytes + maxLengthLeft -= numBytesRead; + bytesAvailable = stream.available(); + } + free(buf); + return true; +} + +// Pad the input according to SHA2 specification +void SHA2Builder::pad() { + // Calculate the number of bytes we have + uint64_t bit_length = total_length * 8; + + // Add the bit '1' to the message + buffer[buffer_size++] = 0x80; + + // Pad with zeros until we have enough space for the length + while (buffer_size + 8 > block_size) { + if (buffer_size < block_size) { + buffer[buffer_size++] = 0x00; + } else { + // Process the block + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + buffer_size = 0; + } + } + + // Pad with zeros to make room for the length + while (buffer_size + 8 < block_size) { + buffer[buffer_size++] = 0x00; + } + + // Add the length in bits + if (is_sha512) { + // For SHA-512, length is 128 bits (16 bytes) + // We only use the lower 64 bits for now + for (int i = 0; i < 8; i++) { + buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8)); + } + // Set the upper 64 bits to 0 (for SHA-384/512, length is limited to 2^128-1) + for (int i = 0; i < 8; i++) { + buffer[block_size - 16 + i] = 0x00; + } + } else { + // For SHA-256, length is 64 bits (8 bytes) + for (int i = 0; i < 8; i++) { + buffer[block_size - 8 + i] = (uint8_t)(bit_length >> (56 - i * 8)); + } + } +} + +// Finalize the hash computation +void SHA2Builder::calculate() { + if (finalized) { + return; + } + + // Pad the input + pad(); + + // Process the final block + if (is_sha512) { + process_block_sha512(buffer); + } else { + process_block_sha256(buffer); + } + + // Extract bytes from the state + if (is_sha512) { + for (size_t i = 0; i < hash_size; i++) { + hash[i] = (uint8_t)(state_64[i >> 3] >> (56 - ((i & 0x7) << 3))); + } + } else { + for (size_t i = 0; i < hash_size; i++) { + hash[i] = (uint8_t)(state_32[i >> 2] >> (24 - ((i & 0x3) << 3))); + } + } + + finalized = true; +} + +// Get the hash as bytes +void SHA2Builder::getBytes(uint8_t *output) { + memcpy(output, hash, hash_size); +} + +// Get the hash as hex string +void SHA2Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA2 not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, hash_size * 2 + 1, hash, hash_size); +} + +// Get the hash as String +String SHA2Builder::toString() { + char out[(hash_size * 2) + 1]; + getChars(out); + return String(out); +} diff --git a/libraries/Hash/src/SHA2Builder.h b/libraries/Hash/src/SHA2Builder.h new file mode 100644 index 00000000000..998e624bcfc --- /dev/null +++ b/libraries/Hash/src/SHA2Builder.h @@ -0,0 +1,96 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SHA2Builder_h +#define SHA2Builder_h + +#include +#include + +#include "HashBuilder.h" + +// SHA2 constants +#define SHA2_224_HASH_SIZE 28 +#define SHA2_256_HASH_SIZE 32 +#define SHA2_384_HASH_SIZE 48 +#define SHA2_512_HASH_SIZE 64 + +#define SHA2_224_BLOCK_SIZE 64 +#define SHA2_256_BLOCK_SIZE 64 +#define SHA2_384_BLOCK_SIZE 128 +#define SHA2_512_BLOCK_SIZE 128 + +// SHA2 state sizes (in 32-bit words for SHA-224/256, 64-bit words for SHA-384/512) +#define SHA2_224_STATE_SIZE 8 +#define SHA2_256_STATE_SIZE 8 +#define SHA2_384_STATE_SIZE 8 +#define SHA2_512_STATE_SIZE 8 + +class SHA2Builder : public HashBuilder { +protected: + uint32_t state_32[8]; // SHA-224/256 state (256 bits) + uint64_t state_64[8]; // SHA-384/512 state (512 bits) + uint8_t buffer[128]; // Input buffer (max block size) + size_t block_size; // Block size + size_t hash_size; // Output hash size + size_t buffer_size; // Current buffer size + bool finalized; // Whether hash has been finalized + bool is_sha512; // Whether using SHA-512 family + uint8_t hash[64]; // Hash result + uint64_t total_length; // Total length of input data + + void process_block_sha256(const uint8_t *data); + void process_block_sha512(const uint8_t *data); + void pad(); + +public: + using HashBuilder::add; + + SHA2Builder(size_t hash_size = SHA2_256_HASH_SIZE); + virtual ~SHA2Builder() {} + + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + + size_t getHashSize() const override { + return hash_size; + } +}; + +class SHA224Builder : public SHA2Builder { +public: + SHA224Builder() : SHA2Builder(SHA2_224_HASH_SIZE) {} +}; + +class SHA256Builder : public SHA2Builder { +public: + SHA256Builder() : SHA2Builder(SHA2_256_HASH_SIZE) {} +}; + +class SHA384Builder : public SHA2Builder { +public: + SHA384Builder() : SHA2Builder(SHA2_384_HASH_SIZE) {} +}; + +class SHA512Builder : public SHA2Builder { +public: + SHA512Builder() : SHA2Builder(SHA2_512_HASH_SIZE) {} +}; + +#endif diff --git a/libraries/Hash/src/SHA3Builder.cpp b/libraries/Hash/src/SHA3Builder.cpp new file mode 100644 index 00000000000..6108aefe4c0 --- /dev/null +++ b/libraries/Hash/src/SHA3Builder.cpp @@ -0,0 +1,267 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "esp32-hal-log.h" +#include "SHA3Builder.h" + +// Keccak round constants +static const uint64_t keccak_round_constants[24] = {0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808AULL, 0x8000000080008000ULL, + 0x000000000000808BULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, + 0x000000000000008AULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000AULL, + 0x000000008000808BULL, 0x800000000000008BULL, 0x8000000000008089ULL, 0x8000000000008003ULL, + 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800AULL, 0x800000008000000AULL, + 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL}; + +// Rho rotation constants +static const uint32_t rho[6] = {0x3f022425, 0x1c143a09, 0x2c3d3615, 0x27191713, 0x312b382e, 0x3e030832}; + +// Pi permutation constants +static const uint32_t pi[6] = {0x110b070a, 0x10050312, 0x04181508, 0x0d13170f, 0x0e14020c, 0x01060916}; + +// Macros for bit manipulation +#define ROTR64(x, y) (((x) << (64U - (y))) | ((x) >> (y))) + +// Keccak-f permutation +void SHA3Builder::keccak_f(uint64_t state[25]) { + uint64_t lane[5]; + uint64_t *s = state; + int i; + + for (int round = 0; round < 24; round++) { + uint64_t t; + + // Theta step + for (i = 0; i < 5; i++) { + lane[i] = s[i] ^ s[i + 5] ^ s[i + 10] ^ s[i + 15] ^ s[i + 20]; + } + for (i = 0; i < 5; i++) { + t = lane[(i + 4) % 5] ^ ROTR64(lane[(i + 1) % 5], 63); + s[i] ^= t; + s[i + 5] ^= t; + s[i + 10] ^= t; + s[i + 15] ^= t; + s[i + 20] ^= t; + } + + // Rho step + for (i = 1; i < 25; i += 4) { + uint32_t r = rho[(i - 1) >> 2]; + for (int j = i; j < i + 4; j++) { + uint8_t r8 = (uint8_t)(r >> 24); + r <<= 8; + s[j] = ROTR64(s[j], r8); + } + } + + // Pi step + t = s[1]; + for (i = 0; i < 24; i += 4) { + uint32_t p = pi[i >> 2]; + for (unsigned j = 0; j < 4; j++) { + uint64_t tmp = s[p & 0xff]; + s[p & 0xff] = t; + t = tmp; + p >>= 8; + } + } + + // Chi step + for (i = 0; i <= 20; i += 5) { + lane[0] = s[i]; + lane[1] = s[i + 1]; + lane[2] = s[i + 2]; + lane[3] = s[i + 3]; + lane[4] = s[i + 4]; + s[i + 0] ^= (~lane[1]) & lane[2]; + s[i + 1] ^= (~lane[2]) & lane[3]; + s[i + 2] ^= (~lane[3]) & lane[4]; + s[i + 3] ^= (~lane[4]) & lane[0]; + s[i + 4] ^= (~lane[0]) & lane[1]; + } + + // Iota step + s[0] ^= keccak_round_constants[round]; + } +} + +// Process a block of data +void SHA3Builder::process_block(const uint8_t *data) { + // XOR the data into the state using byte-level operations + for (size_t i = 0; i < rate; i++) { + size_t state_idx = i >> 3; // i / 8 + size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8 + uint64_t byte_val = (uint64_t)data[i] << bit_offset; + state[state_idx] ^= byte_val; + } + + // Apply Keccak-f permutation + keccak_f(state); +} + +// Pad the input according to SHA3 specification +void SHA3Builder::pad() { + // Clear the buffer first + memset(buffer + buffer_size, 0, rate - buffer_size); + + // Add the domain separator (0x06) at the current position + buffer[buffer_size] = 0x06; + + // Set the last byte to indicate the end (0x80) + buffer[rate - 1] = 0x80; +} + +// Constructor +SHA3Builder::SHA3Builder(size_t hash_size) : hash_size(hash_size), buffer_size(0), finalized(false) { + // Calculate rate based on hash size + if (hash_size == SHA3_224_HASH_SIZE) { + rate = SHA3_224_RATE; + } else if (hash_size == SHA3_256_HASH_SIZE) { + rate = SHA3_256_RATE; + } else if (hash_size == SHA3_384_HASH_SIZE) { + rate = SHA3_384_RATE; + } else if (hash_size == SHA3_512_HASH_SIZE) { + rate = SHA3_512_RATE; + } else { + log_e("Invalid hash size: %d", hash_size); + rate = 0; // Invalid hash size + } +} + +// Initialize the hash computation +void SHA3Builder::begin() { + // Clear the state + memset(state, 0, sizeof(state)); + memset(buffer, 0, sizeof(buffer)); + buffer_size = 0; + finalized = false; +} + +// Add data to the hash computation +void SHA3Builder::add(const uint8_t *data, size_t len) { + if (finalized || len == 0) { + return; + } + + size_t offset = 0; + + // Process any buffered data first + if (buffer_size > 0) { + size_t to_copy = std::min(len, rate - buffer_size); + memcpy(buffer + buffer_size, data, to_copy); + buffer_size += to_copy; + offset += to_copy; + + if (buffer_size == rate) { + process_block(buffer); + buffer_size = 0; + } + } + + // Process full blocks + while (offset + rate <= len) { + process_block(data + offset); + offset += rate; + } + + // Buffer remaining data + if (offset < len) { + memcpy(buffer, data + offset, len - offset); + buffer_size = len - offset; + } +} + +// Add data from a stream +bool SHA3Builder::addStream(Stream &stream, const size_t maxLen) { + const int buf_size = 512; + int maxLengthLeft = maxLen; + uint8_t *buf = (uint8_t *)malloc(buf_size); + + if (!buf) { + return false; + } + + int bytesAvailable = stream.available(); + while ((bytesAvailable > 0) && (maxLengthLeft > 0)) { + // Determine number of bytes to read + int readBytes = bytesAvailable; + if (readBytes > maxLengthLeft) { + readBytes = maxLengthLeft; + } + if (readBytes > buf_size) { + readBytes = buf_size; + } + + // Read data and check if we got something + int numBytesRead = stream.readBytes(buf, readBytes); + if (numBytesRead < 1) { + free(buf); + return false; + } + + // Update SHA3 with buffer payload + add(buf, numBytesRead); + + // Update available number of bytes + maxLengthLeft -= numBytesRead; + bytesAvailable = stream.available(); + } + free(buf); + return true; +} + +// Finalize the hash computation +void SHA3Builder::calculate() { + if (finalized) { + return; + } + + // Pad the input + pad(); + + // Process the final block + process_block(buffer); + + // Extract bytes from the state + for (size_t i = 0; i < hash_size; i++) { + size_t state_idx = i >> 3; // i / 8 + size_t bit_offset = (i & 0x7) << 3; // (i % 8) * 8 + hash[i] = (uint8_t)(state[state_idx] >> bit_offset); + } + + finalized = true; +} + +// Get the hash as bytes +void SHA3Builder::getBytes(uint8_t *output) { + memcpy(output, hash, hash_size); +} + +// Get the hash as hex string +void SHA3Builder::getChars(char *output) { + if (!finalized || output == nullptr) { + log_e("Error: SHA3 not calculated or no output buffer provided."); + return; + } + + bytes2hex(output, hash_size * 2 + 1, hash, hash_size); +} + +// Get the hash as String +String SHA3Builder::toString() { + char out[(hash_size * 2) + 1]; + getChars(out); + return String(out); +} diff --git a/libraries/Hash/src/SHA3Builder.h b/libraries/Hash/src/SHA3Builder.h new file mode 100644 index 00000000000..64663fb2683 --- /dev/null +++ b/libraries/Hash/src/SHA3Builder.h @@ -0,0 +1,89 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SHA3Builder_h +#define SHA3Builder_h + +#include +#include + +#include "HashBuilder.h" + +// SHA3 constants +#define SHA3_224_HASH_SIZE 28 +#define SHA3_256_HASH_SIZE 32 +#define SHA3_384_HASH_SIZE 48 +#define SHA3_512_HASH_SIZE 64 + +#define SHA3_224_RATE 144 +#define SHA3_256_RATE 136 +#define SHA3_384_RATE 104 +#define SHA3_512_RATE 72 + +#define SHA3_STATE_SIZE 200 // 1600 bits = 200 bytes + +class SHA3Builder : public HashBuilder { +protected: + uint64_t state[25]; // SHA3 state (1600 bits) + uint8_t buffer[200]; // Input buffer + size_t rate; // Rate (block size) + size_t hash_size; // Output hash size + size_t buffer_size; // Current buffer size + bool finalized; // Whether hash has been finalized + uint8_t hash[64]; // Hash result + + void keccak_f(uint64_t state[25]); + void process_block(const uint8_t *data); + void pad(); + +public: + using HashBuilder::add; + + SHA3Builder(size_t hash_size = SHA3_256_HASH_SIZE); + virtual ~SHA3Builder() {} + + void begin() override; + void add(const uint8_t *data, size_t len) override; + bool addStream(Stream &stream, const size_t maxLen) override; + void calculate() override; + void getBytes(uint8_t *output) override; + void getChars(char *output) override; + String toString() override; + + size_t getHashSize() const override { + return hash_size; + } +}; + +class SHA3_224Builder : public SHA3Builder { +public: + SHA3_224Builder() : SHA3Builder(SHA3_224_HASH_SIZE) {} +}; + +class SHA3_256Builder : public SHA3Builder { +public: + SHA3_256Builder() : SHA3Builder(SHA3_256_HASH_SIZE) {} +}; + +class SHA3_384Builder : public SHA3Builder { +public: + SHA3_384Builder() : SHA3Builder(SHA3_384_HASH_SIZE) {} +}; + +class SHA3_512Builder : public SHA3Builder { +public: + SHA3_512Builder() : SHA3Builder(SHA3_512_HASH_SIZE) {} +}; + +#endif diff --git a/libraries/Insights/src/Insights.h b/libraries/Insights/src/Insights.h index 2e09818be94..0515d9df5da 100644 --- a/libraries/Insights/src/Insights.h +++ b/libraries/Insights/src/Insights.h @@ -101,7 +101,9 @@ class ESPInsightsClass { bool event(const char *tag, const char *format, ...); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_INSIGHTS) extern ESPInsightsClass Insights; +#endif extern "C" { #endif diff --git a/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino b/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino index f169ac24954..003e10638de 100644 --- a/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino +++ b/libraries/LittleFS/examples/LITTLEFS_time/LITTLEFS_time.ino @@ -40,10 +40,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -54,10 +55,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/LittleFS/src/LittleFS.h b/libraries/LittleFS/src/LittleFS.h index da4ab7d1f6f..bf32bb442d0 100644 --- a/libraries/LittleFS/src/LittleFS.h +++ b/libraries/LittleFS/src/LittleFS.h @@ -38,7 +38,9 @@ class LittleFSFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_LITTLEFS) extern fs::LittleFSFS LittleFS; +#endif #endif /* CONFIG_LITTLEFS_PAGE_SIZE */ #endif diff --git a/libraries/Matter/src/Matter.h b/libraries/Matter/src/Matter.h index 09e59b4e04b..89360e81d4b 100644 --- a/libraries/Matter/src/Matter.h +++ b/libraries/Matter/src/Matter.h @@ -203,6 +203,8 @@ class ArduinoMatter { static void _init(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_MATTER) extern ArduinoMatter Matter; +#endif #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ diff --git a/libraries/Network/src/NetworkEvents.cpp b/libraries/Network/src/NetworkEvents.cpp index 161e55c5d01..195499c5138 100644 --- a/libraries/Network/src/NetworkEvents.cpp +++ b/libraries/Network/src/NetworkEvents.cpp @@ -3,6 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ + +#include //std::nothrow + #include "NetworkEvents.h" #include "NetworkManager.h" #include "esp_task.h" @@ -82,7 +85,7 @@ bool NetworkEvents::postEvent(const arduino_event_t *data) { if (data == NULL || _arduino_event_queue == NULL) { return false; } - arduino_event_t *event = new arduino_event_t(); + arduino_event_t *event = new (std::nothrow) arduino_event_t(); if (event == NULL) { log_e("Arduino Event Malloc Failed!"); return false; diff --git a/libraries/Network/src/NetworkManager.h b/libraries/Network/src/NetworkManager.h index 6b9d5e16cfc..dafac9cd983 100644 --- a/libraries/Network/src/NetworkManager.h +++ b/libraries/Network/src/NetworkManager.h @@ -31,4 +31,6 @@ class NetworkManager : public NetworkEvents, public Printable { } }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_NETWORK) extern NetworkManager Network; +#endif diff --git a/libraries/OpenThread/src/OThread.h b/libraries/OpenThread/src/OThread.h index 6e21b854574..e477eec19fe 100644 --- a/libraries/OpenThread/src/OThread.h +++ b/libraries/OpenThread/src/OThread.h @@ -162,7 +162,9 @@ class OpenThread { void populateMulticastAddressCache() const; }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OPENTHREAD) extern OpenThread OThread; +#endif #endif /* CONFIG_OPENTHREAD_ENABLED */ #endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/OpenThread/src/OThreadCLI.h b/libraries/OpenThread/src/OThreadCLI.h index 788edc2709b..cb2f2717d4f 100644 --- a/libraries/OpenThread/src/OThreadCLI.h +++ b/libraries/OpenThread/src/OThreadCLI.h @@ -68,7 +68,9 @@ class OpenThreadCLI : public Stream { void flush(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_OPENTHREADCLI) extern OpenThreadCLI OThreadCLI; +#endif #endif /* CONFIG_OPENTHREAD_ENABLED */ #endif /* SOC_IEEE802154_SUPPORTED */ diff --git a/libraries/PPP/src/PPP.h b/libraries/PPP/src/PPP.h index b317f52aefc..189825b61a1 100644 --- a/libraries/PPP/src/PPP.h +++ b/libraries/PPP/src/PPP.h @@ -113,5 +113,8 @@ class PPPClass : public NetworkInterface { static bool pppDetachBus(void *bus_pointer); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_PPP) extern PPPClass PPP; +#endif + #endif /* CONFIG_LWIP_PPP_SUPPORT && ARDUINO_HAS_ESP_MODEM */ diff --git a/libraries/RainMaker/src/RMaker.h b/libraries/RainMaker/src/RMaker.h index 21c5b0d1832..0e16e6ee387 100644 --- a/libraries/RainMaker/src/RMaker.h +++ b/libraries/RainMaker/src/RMaker.h @@ -39,5 +39,8 @@ class RMakerClass { esp_err_t stop(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_RAINMAKER) extern RMakerClass RMaker; #endif + +#endif diff --git a/libraries/SD/examples/SD_time/SD_time.ino b/libraries/SD/examples/SD_time/SD_time.ino index cc65d21ae20..48d119bebc4 100644 --- a/libraries/SD/examples/SD_time/SD_time.ino +++ b/libraries/SD/examples/SD_time/SD_time.ino @@ -78,10 +78,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -92,10 +93,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SD/src/SD.h b/libraries/SD/src/SD.h index d8252ee44f7..f4311d25a11 100644 --- a/libraries/SD/src/SD.h +++ b/libraries/SD/src/SD.h @@ -43,7 +43,9 @@ class SDFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD) extern fs::SDFS SD; +#endif using namespace fs; typedef fs::File SDFile; diff --git a/libraries/SD/src/sd_diskio.cpp b/libraries/SD/src/sd_diskio.cpp index 40d6ede9f81..f33c5c79b12 100644 --- a/libraries/SD/src/sd_diskio.cpp +++ b/libraries/SD/src/sd_diskio.cpp @@ -462,23 +462,52 @@ struct AcquireSPI { * FATFS API * */ +/** + * @brief Initialize an SD card for use with FatFs + * + * This function implements the complete SD card initialization sequence according to + * the SD card specification. It performs card detection, type identification, + * and configuration for SPI mode operation. + * + * The initialization sequence follows the SD card protocol: + * 1. Power-up sequence with 74+ clock cycles + * 2. GO_IDLE_STATE command to reset the card + * 3. CRC_ON_OFF to enable/disable CRC checking + * 4. SEND_IF_COND to identify SDHC/SDXC cards + * 5. APP_OP_COND to set operating conditions + * 6. Card type detection (SD/SDHC/MMC) + * 7. Final configuration and sector count retrieval + * + * @param pdrv Physical drive number (0-9) + * @return DSTATUS Status of the initialization (0 = success, STA_NOINIT = failed) + */ DSTATUS ff_sd_initialize(uint8_t pdrv) { char token; unsigned int resp; unsigned int start; + + // Get the card structure for the given drive number ardu_sdcard_t *card = s_cards[pdrv]; + // If the card is already initialized, return its current status if (!(card->status & STA_NOINIT)) { return card->status; } + // Lock the SPI bus and set it to a low frequency (400kHz) for initialization + // Low frequency is required during initialization for reliable communication AcquireSPI card_locked(card, 400000); + // Step 1: Power-up sequence - Send at least 74 clock cycles with CS high and MOSI high + // This is required by the SD card specification to ensure proper card state reset + // We send 20 bytes (160 clock cycles) to exceed the minimum requirement digitalWrite(card->ssPin, HIGH); for (uint8_t i = 0; i < 20; i++) { card->spi->transfer(0XFF); } + // Step 2: Select the card and send GO_IDLE_STATE command + // This command resets the card to idle state and enables SPI mode // Fix mount issue - sdWait fail ignored before command GO_IDLE_STATE digitalWrite(card->ssPin, LOW); if (!sdWait(pdrv, 500)) { @@ -491,26 +520,34 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } sdDeselectCard(pdrv); + // Step 3: Configure CRC checking + // Enable CRC for data transfers in SPI mode (required for reliable communication) token = sdTransaction(pdrv, CRC_ON_OFF, 1, NULL); if (token == 0x5) { - //old card maybe + // Old card that doesn't support CRC - disable CRC checking card->supports_crc = false; } else if (token != 1) { log_w("CRC_ON_OFF failed: %u", token); goto unknown_card; } + // Step 4: Card type detection and initialization + // Try to identify SDHC/SDXC cards using SEND_IF_COND command if (sdTransaction(pdrv, SEND_IF_COND, 0x1AA, &resp) == 1) { + // Card responded to SEND_IF_COND - likely SDHC/SDXC if ((resp & 0xFFF) != 0x1AA) { log_w("SEND_IF_COND failed: %03X", resp & 0xFFF); goto unknown_card; } + // Read Operating Conditions Register to check card capabilities if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { log_w("READ_OCR failed: %X", resp); goto unknown_card; } + // Send APP_OP_COND to set operating conditions for SDHC/SDXC + // Wait up to 1 second for the card to become ready start = millis(); do { token = sdTransaction(pdrv, APP_OP_COND, 0x40100000, NULL); @@ -521,37 +558,41 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { goto unknown_card; } + // Determine if it's SDHC (high capacity) or regular SD if (!sdTransaction(pdrv, READ_OCR, 0, &resp)) { if (resp & (1 << 30)) { - card->type = CARD_SDHC; + card->type = CARD_SDHC; // High capacity card (SDHC/SDXC) } else { - card->type = CARD_SD; + card->type = CARD_SD; // Standard capacity card } } else { log_w("READ_OCR failed: %X", resp); goto unknown_card; } } else { + // Card didn't respond to SEND_IF_COND - try SD or MMC initialization if (sdTransaction(pdrv, READ_OCR, 0, &resp) != 1 || !(resp & (1 << 20))) { log_w("READ_OCR failed: %X", resp); goto unknown_card; } + // Try SD card initialization first start = millis(); do { token = sdTransaction(pdrv, APP_OP_COND, 0x100000, NULL); } while (token == 0x01 && (millis() - start) < 1000); if (!token) { - card->type = CARD_SD; + card->type = CARD_SD; // Standard SD card } else { + // Try MMC card initialization start = millis(); do { token = sdTransaction(pdrv, SEND_OP_COND, 0x100000, NULL); } while (token != 0x00 && (millis() - start) < 1000); if (token == 0x00) { - card->type = CARD_MMC; + card->type = CARD_MMC; // MMC card } else { log_w("SEND_OP_COND failed: %u", token); goto unknown_card; @@ -559,6 +600,7 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 5: Clear card detection for SD cards (not needed for MMC) if (card->type != CARD_MMC) { if (sdTransaction(pdrv, APP_CLR_CARD_DETECT, 0, NULL)) { log_w("APP_CLR_CARD_DETECT failed"); @@ -566,6 +608,8 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 6: Set block length for non-SDHC cards + // SDHC cards have fixed 512-byte blocks, others need explicit block length setting if (card->type != CARD_SDHC) { if (sdTransaction(pdrv, SET_BLOCKLEN, 512, NULL) != 0x00) { log_w("SET_BLOCKLEN failed"); @@ -573,16 +617,20 @@ DSTATUS ff_sd_initialize(uint8_t pdrv) { } } + // Step 7: Get card capacity and finalize initialization card->sectors = sdGetSectorsCount(pdrv); + // Limit frequency to 25MHz for compatibility (SD spec maximum for non-UHS cards) if (card->frequency > 25000000) { card->frequency = 25000000; } + // Mark card as initialized card->status &= ~STA_NOINIT; return card->status; unknown_card: + // Mark card as unknown type if initialization failed card->type = CARD_UNKNOWN; return card->status; } diff --git a/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino b/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino index d1e933e4f4b..ef52089dd38 100644 --- a/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino +++ b/libraries/SD_MMC/examples/SDMMC_time/SDMMC_time.ino @@ -83,10 +83,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -97,10 +98,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SD_MMC/src/SD_MMC.h b/libraries/SD_MMC/src/SD_MMC.h index b6fe13a0d24..7192d466b89 100644 --- a/libraries/SD_MMC/src/SD_MMC.h +++ b/libraries/SD_MMC/src/SD_MMC.h @@ -77,7 +77,9 @@ class SDMMCFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SD_MMC) extern fs::SDMMCFS SD_MMC; +#endif #endif /* SOC_SDMMC_HOST_SUPPORTED */ #endif /* _SDMMC_H_ */ diff --git a/libraries/SPI/src/SPI.h b/libraries/SPI/src/SPI.h index 6c300e53df2..c00b8155bb9 100644 --- a/libraries/SPI/src/SPI.h +++ b/libraries/SPI/src/SPI.h @@ -98,7 +98,9 @@ class SPIClass { } }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI) extern SPIClass SPI; +#endif #endif /* SOC_GPSPI_SUPPORTED */ #endif /* _SPI_H_INCLUDED */ diff --git a/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino b/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino index 78a66a94e75..79d8101ae66 100644 --- a/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino +++ b/libraries/SPIFFS/examples/SPIFFS_time/SPIFFS_time.ino @@ -28,10 +28,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" DIR : "); Serial.print(file.name()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); if (levels) { listDir(fs, file.path(), levels - 1); @@ -42,10 +43,11 @@ void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); - struct tm *tmstruct = localtime(&t); + struct tm tmstruct; + localtime_r(&t, &tmstruct); Serial.printf( - " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, - tmstruct->tm_min, tmstruct->tm_sec + " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct.tm_year) + 1900, (tmstruct.tm_mon) + 1, tmstruct.tm_mday, tmstruct.tm_hour, tmstruct.tm_min, + tmstruct.tm_sec ); } file = root.openNextFile(); diff --git a/libraries/SPIFFS/src/SPIFFS.h b/libraries/SPIFFS/src/SPIFFS.h index 2e7bf6de7d8..aad4743bb19 100644 --- a/libraries/SPIFFS/src/SPIFFS.h +++ b/libraries/SPIFFS/src/SPIFFS.h @@ -34,6 +34,8 @@ class SPIFFSFS : public FS { } // namespace fs +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPIFFS) extern fs::SPIFFSFS SPIFFS; +#endif #endif diff --git a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino index a92e576196d..69536bed7e2 100644 --- a/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino +++ b/libraries/SimpleBLE/examples/SimpleBleDevice/SimpleBleDevice.ino @@ -18,10 +18,6 @@ #include "SimpleBLE.h" -#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED) -#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it -#endif - SimpleBLE ble; void onButton() { @@ -34,7 +30,7 @@ void onButton() { void setup() { Serial.begin(115200); Serial.setDebugOutput(true); - pinMode(0, INPUT_PULLUP); + pinMode(BOOT_PIN, INPUT_PULLUP); Serial.print("ESP32 SDK: "); Serial.println(ESP.getSdkVersion()); ble.begin("ESP32 SimpleBLE"); @@ -43,7 +39,7 @@ void setup() { void loop() { static uint8_t lastPinState = 1; - uint8_t pinState = digitalRead(0); + uint8_t pinState = digitalRead(BOOT_PIN); if (!pinState && lastPinState) { onButton(); } diff --git a/libraries/SimpleBLE/src/SimpleBLE.cpp b/libraries/SimpleBLE/src/SimpleBLE.cpp index 3f1f2bbd1c1..670d371ca9e 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.cpp +++ b/libraries/SimpleBLE/src/SimpleBLE.cpp @@ -12,20 +12,56 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include "SimpleBLE.h" #include "esp32-hal-log.h" +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif + +/*************************************************************************** + * Bluedroid includes * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" +#endif + +/*************************************************************************** + * NimBLE includes * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_BT_NIMBLE_LEGACY_VHCI_ENABLE +#include +#endif + +// Forward declaration +extern "C" void ble_store_config_init(void); +#endif + +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#include "esp32-hal-hosted.h" +#endif +/*************************************************************************** + * Bluedroid data structures * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static esp_ble_adv_data_t _adv_config = { .set_scan_rsp = false, .include_name = true, @@ -55,14 +91,87 @@ static esp_ble_adv_params_t _adv_params = { .channel_map = ADV_CHNL_ALL, .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, }; +#endif + +/*************************************************************************** + * NimBLE data structures * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static struct ble_hs_adv_fields _nimble_adv_fields; +static struct ble_gap_adv_params _nimble_adv_params = { + .conn_mode = BLE_GAP_CONN_MODE_NON, + .disc_mode = BLE_GAP_DISC_MODE_GEN, + .itvl_min = 512, + .itvl_max = 1024, + .channel_map = 0, + .filter_policy = 0, + .high_duty_cycle = 0, +}; +// Global variables for NimBLE synchronization +static bool _nimble_synced = false; +#endif + +// Global state tracking +static bool _ble_initialized = false; + +/*************************************************************************** + * Bluedroid callbacks * + ***************************************************************************/ +#if defined(CONFIG_BLUEDROID_ENABLED) static void _on_gap(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) { if (event == ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT) { esp_ble_gap_start_advertising(&_adv_params); } } +#endif + +/*************************************************************************** + * NimBLE callbacks * + ***************************************************************************/ +#if defined(CONFIG_NIMBLE_ENABLED) +static void _nimble_host_task(void *param) { + // This function will be called to run the BLE host + nimble_port_run(); + // Should never reach here unless nimble_port_stop() is called + nimble_port_freertos_deinit(); +} + +static void _nimble_on_reset(int reason) { + log_i("NimBLE reset; reason=%d", reason); +} + +static void _nimble_on_sync(void) { + log_i("NimBLE sync complete"); + _nimble_synced = true; +} + +static int _nimble_gap_event(struct ble_gap_event *event, void *arg) { + switch (event->type) { + case BLE_GAP_EVENT_ADV_COMPLETE: log_d("BLE_GAP_EVENT_ADV_COMPLETE"); break; + default: break; + } + return 0; +} +#endif +/*************************************************************************** + * Forward declarations * + ***************************************************************************/ +static bool _init_gap(const char *name); +static bool _stop_gap(); +static bool _update_advertising(const char *name); + +/*************************************************************************** + * Initialization functions * + ***************************************************************************/ static bool _init_gap(const char *name) { + if (_ble_initialized) { + log_d("BLE already initialized, skipping"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (!btStarted() && !btStart()) { log_e("btStart failed"); return false; @@ -92,16 +201,178 @@ static bool _init_gap(const char *name) { log_e("gap_register_callback failed"); return false; } + _ble_initialized = true; return true; +#elif defined(CONFIG_NIMBLE_ENABLED) +#if defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) + // Initialize esp-hosted transport for BLE HCI when explicitly enabled + if (!hostedInitBLE()) { + log_e("Failed to initialize ESP-Hosted for BLE"); + return false; + } +#endif + + esp_err_t errRc = nimble_port_init(); + if (errRc != ESP_OK) { + log_e("nimble_port_init: rc=%d", errRc); + return false; + } + + // Configure NimBLE host + ble_hs_cfg.reset_cb = _nimble_on_reset; + ble_hs_cfg.sync_cb = _nimble_on_sync; + ble_hs_cfg.sm_io_cap = BLE_HS_IO_NO_INPUT_OUTPUT; + ble_hs_cfg.sm_bonding = 0; + ble_hs_cfg.sm_mitm = 0; + ble_hs_cfg.sm_sc = 1; + + // Set device name + errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Configure advertising data + memset(&_nimble_adv_fields, 0, sizeof(_nimble_adv_fields)); + _nimble_adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP; + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + _nimble_adv_fields.tx_pwr_lvl_is_present = 1; + + // Initialize store configuration + ble_store_config_init(); + + // Start the host task + nimble_port_freertos_init(_nimble_host_task); + + // Wait for sync + int sync_timeout = 1000; // 10 seconds timeout + while (!_nimble_synced && sync_timeout > 0) { + vTaskDelay(pdMS_TO_TICKS(10)); + sync_timeout--; + } + + if (!_nimble_synced) { + log_e("NimBLE sync timeout"); + return false; + } + + // Set advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Start advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + _ble_initialized = true; + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } static bool _stop_gap() { + if (!_ble_initialized) { + log_d("BLE not initialized, nothing to stop"); + return true; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) if (btStarted()) { esp_bluedroid_disable(); esp_bluedroid_deinit(); btStop(); } + _ble_initialized = false; + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop advertising + ble_gap_adv_stop(); + + // Stop NimBLE + int rc = nimble_port_stop(); + if (rc != ESP_OK) { + log_e("nimble_port_stop: rc=%d", rc); + } + + nimble_port_deinit(); + _nimble_synced = false; + _ble_initialized = false; + return true; +#else + return true; +#endif +} + +static bool _update_advertising(const char *name) { + if (!_ble_initialized) { + log_e("BLE not initialized"); + return false; + } + +#if defined(CONFIG_BLUEDROID_ENABLED) + // Stop current advertising + esp_ble_gap_stop_advertising(); + + // Set new device name + if (esp_ble_gap_set_device_name(name)) { + log_e("gap_set_device_name failed"); + return false; + } + + // Restart advertising with new name + if (esp_ble_gap_config_adv_data(&_adv_config)) { + log_e("gap_config_adv_data failed"); + return false; + } + + return true; +#elif defined(CONFIG_NIMBLE_ENABLED) + // Stop current advertising + ble_gap_adv_stop(); + + // Set new device name + int errRc = ble_svc_gap_device_name_set(name); + if (errRc != ESP_OK) { + log_e("ble_svc_gap_device_name_set: rc=%d", errRc); + return false; + } + + // Update advertising fields with new name + _nimble_adv_fields.name = (uint8_t *)name; + _nimble_adv_fields.name_len = strlen(name); + _nimble_adv_fields.name_is_complete = 1; + + // Set new advertising data + errRc = ble_gap_adv_set_fields(&_nimble_adv_fields); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_set_fields: rc=%d", errRc); + return false; + } + + // Restart advertising + errRc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, BLE_HS_FOREVER, &_nimble_adv_params, _nimble_gap_event, NULL); + if (errRc != ESP_OK) { + log_e("ble_gap_adv_start: rc=%d", errRc); + return false; + } + + return true; +#else + log_e("No BLE stack enabled"); + return false; +#endif } /* @@ -121,6 +392,12 @@ bool SimpleBLE::begin(String localName) { if (localName.length()) { local_name = localName; } + + // If already initialized, just update advertising data + if (_ble_initialized) { + return _update_advertising(local_name.c_str()); + } + return _init_gap(local_name.c_str()); } @@ -128,4 +405,5 @@ void SimpleBLE::end() { _stop_gap(); } -#endif +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE diff --git a/libraries/SimpleBLE/src/SimpleBLE.h b/libraries/SimpleBLE/src/SimpleBLE.h index df1ee751f10..41a06b0b1aa 100644 --- a/libraries/SimpleBLE/src/SimpleBLE.h +++ b/libraries/SimpleBLE/src/SimpleBLE.h @@ -15,10 +15,10 @@ #ifndef _SIMPLE_BLE_H_ #define _SIMPLE_BLE_H_ -#include "sdkconfig.h" #include "soc/soc_caps.h" - -#if SOC_BT_SUPPORTED && defined(CONFIG_BT_ENABLED) && defined(CONFIG_BLUEDROID_ENABLED) +#include "sdkconfig.h" +#if defined(SOC_BLE_SUPPORTED) || defined(CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE) +#if defined(CONFIG_BLUEDROID_ENABLED) || defined(CONFIG_NIMBLE_ENABLED) #include #include @@ -26,7 +26,10 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" + +#if defined(SOC_BLE_SUPPORTED) #include "esp_bt.h" +#endif #include "Arduino.h" @@ -60,6 +63,7 @@ class SimpleBLE { private: }; -#endif +#endif // SOC_BLE_SUPPORTED || CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE +#endif // CONFIG_BLUEDROID_ENABLED || CONFIG_NIMBLE_ENABLED -#endif +#endif // _SIMPLE_BLE_H_ diff --git a/libraries/USB/examples/MIDI/MidiController/MidiController.ino b/libraries/USB/examples/MIDI/MidiController/MidiController.ino index 2871d3b1a52..9cdd8a805de 100644 --- a/libraries/USB/examples/MIDI/MidiController/MidiController.ino +++ b/libraries/USB/examples/MIDI/MidiController/MidiController.ino @@ -20,7 +20,8 @@ void loop() {} #include "USB.h" #include "USBMIDI.h" -USBMIDI MIDI; +// Creates the MIDI device with specific descriptor +USBMIDI MIDI("ESP MIDI Device"); #define MIDI_NOTE_C4 60 diff --git a/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino index e3ad1c4e028..e118d5ddb9a 100644 --- a/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino +++ b/libraries/USB/examples/MIDI/MidiInterface/MidiInterface.ino @@ -24,8 +24,12 @@ void setup() {} void loop() {} #else +// define a new USB MIDI device name using a macro +SET_USB_MIDI_DEVICE_NAME("ESP MIDI Device") + #include "USB.h" #include "USBMIDI.h" +// Creates the MIDI device with specific name defined with the SET_USB_MIDI_DEVICE_NAME() macro USBMIDI MIDI; #define MIDI_RX 39 diff --git a/libraries/USB/src/USBMIDI.cpp b/libraries/USB/src/USBMIDI.cpp index 8a9571855e1..61b75f0144d 100644 --- a/libraries/USB/src/USBMIDI.cpp +++ b/libraries/USB/src/USBMIDI.cpp @@ -6,6 +6,13 @@ #include "Arduino.h" #include "esp32-hal-tinyusb.h" +// Initialize static members +char *USBMIDI::midiUserDeviceName = nullptr; +// Weak definition of getUSBMIDIDefaultDeviceName to provide a default name +__attribute__((weak)) const char *getUSBMIDIDefaultDeviceName() { + return ESP32_USB_MIDI_DEFAULT_NAME; +} + // Default Cable Number (for simplified APIs that do not expose this) #define DEFAULT_CN 0 @@ -18,7 +25,7 @@ extern "C" uint16_t tusb_midi_load_descriptor(uint8_t *dst, uint8_t *itf) { } tinyusb_midi_descriptor_loaded = true; - uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB MIDI"); + uint8_t str_index = tinyusb_add_string_descriptor(USBMIDI::getCurrentDeviceName()); uint8_t ep_in = tinyusb_get_free_in_endpoint(); TU_VERIFY(ep_in != 0); uint8_t ep_out = tinyusb_get_free_out_endpoint(); @@ -41,9 +48,68 @@ USBMIDI::USBMIDI() { } } +// private function for setting a not null/empty MIDI device name limited to 32 characters +void USBMIDI::setDeviceName(const char *name) { + const uint8_t maxNameLength = 32; // tinyUSB Descriptor limit + if (name != nullptr && strlen(name) > 0) { + if (strlen(name) > maxNameLength) { + log_w("USBMIDI: Device name too long, truncating to %d characters.", maxNameLength); + } + if (!midiUserDeviceName) { + midiUserDeviceName = new char[maxNameLength + 1]; // +1 for null-terminator + } + if (midiUserDeviceName) { + strncpy(midiUserDeviceName, name, maxNameLength); + // Ensure null-termination when overflowing + midiUserDeviceName[maxNameLength] = '\0'; + } else { + log_e("USBMIDI: Failed to allocate memory for device name, using default name."); + } + } else { + log_w("USBMIDI: No device name provided, using default name [%s].", getUSBMIDIDefaultDeviceName()); + } +} + +/** +* @brief Constructor for setting the current device name +* 1. Name set via constructor (if any) +* 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) +* 3. Default name "TinyUSB MIDI" +* If device name is set as "", it will be ignored +*/ +USBMIDI::USBMIDI(const char *name) { + if (!tinyusb_midi_interface_enabled) { + setDeviceName(name); + tinyusb_midi_interface_enabled = true; + tinyusb_enable_interface(USB_INTERFACE_MIDI, TUD_MIDI_DESC_LEN, tusb_midi_load_descriptor); + } else { + log_e("USBMIDI: Multiple instances of USBMIDI not supported!"); + } +} + +USBMIDI::~USBMIDI() { + if (midiUserDeviceName) { + delete[] midiUserDeviceName; + midiUserDeviceName = nullptr; + } +} + void USBMIDI::begin() {} void USBMIDI::end() {} +const char *USBMIDI::getCurrentDeviceName(void) { + if (midiUserDeviceName) { + return midiUserDeviceName; + } + // If no user name set, use the compile-time default name limited to 32 characters + setDeviceName(getUSBMIDIDefaultDeviceName()); + if (midiUserDeviceName && strlen(midiUserDeviceName)) { + return midiUserDeviceName; + } else { + return "TinyUSB MIDI"; + } +} + // uint compatible version of constrain #define uconstrain(amt, low, high) ((amt) <= (low) ? (low) : ((amt) > (high) ? (high) : (amt))) @@ -92,7 +158,7 @@ void USBMIDI::channelPressure(uint8_t pressure, uint8_t channel) { // Pitch Bend Change [-8192,0,8191] void USBMIDI::pitchBend(int16_t value, uint8_t channel) { uint16_t pitchBendValue = constrain(value, -8192, 8191) + 8192; - pitchBend(pitchBendValue); + pitchBend(pitchBendValue, channel); } // Pitch Bend Change [0,8192,16383] diff --git a/libraries/USB/src/USBMIDI.h b/libraries/USB/src/USBMIDI.h index 91a1bfa4be1..8f111dbfd4a 100644 --- a/libraries/USB/src/USBMIDI.h +++ b/libraries/USB/src/USBMIDI.h @@ -18,11 +18,41 @@ typedef struct { } midiEventPacket_t; class USBMIDI { +private: + static char *midiUserDeviceName; // user device name + static void setDeviceName(const char *name); // set user device name limited to 32 characters + public: + /** + * @brief Default constructor + * Will use the compile-time name if set via SET_USB_MIDI_DEVICE_NAME(), + * otherwise uses "TinyUSB MIDI" + */ USBMIDI(void); + + /** + * @brief Set the current device name + * 1. Name set via constructor (if any) + * 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) + * 3. Default name "TinyUSB MIDI" + * It has no effect if name is set as NULL or "" + */ + USBMIDI(const char *name); + + ~USBMIDI(); + void begin(void); void end(void); + /** + * @brief Get the current device name + * @return The device name in order of precedence: + * 1. Name set via constructor (if any) + * 2. Name set via SET_USB_MIDI_DEVICE_NAME() macro (if defined) + * 3. Default name "TinyUSB MIDI" + */ + static const char *getCurrentDeviceName(void); + /* User-level API */ // Note On diff --git a/libraries/Update/src/HttpsOTAUpdate.h b/libraries/Update/src/HttpsOTAUpdate.h index d470ad50722..8eadc799db5 100644 --- a/libraries/Update/src/HttpsOTAUpdate.h +++ b/libraries/Update/src/HttpsOTAUpdate.h @@ -27,5 +27,8 @@ class HttpsOTAUpdateClass { HttpsOTAStatus_t status(); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPSOTAUPDATE) extern HttpsOTAUpdateClass HttpsOTA; #endif + +#endif diff --git a/libraries/WebServer/src/detail/mimetable.cpp b/libraries/WebServer/src/detail/mimetable.cpp index 758f3ad34ef..036de07d599 100644 --- a/libraries/WebServer/src/detail/mimetable.cpp +++ b/libraries/WebServer/src/detail/mimetable.cpp @@ -10,6 +10,7 @@ const Entry mimeTable[maxType] = { {".css", "text/css"}, {".txt", "text/plain"}, {".js", "application/javascript"}, + {".mjs", "text/javascript"}, {".json", "application/json"}, {".png", "image/png"}, {".gif", "image/gif"}, diff --git a/libraries/WebServer/src/detail/mimetable.h b/libraries/WebServer/src/detail/mimetable.h index 4732e59c466..869b28ea111 100644 --- a/libraries/WebServer/src/detail/mimetable.h +++ b/libraries/WebServer/src/detail/mimetable.h @@ -9,6 +9,7 @@ enum type { css, txt, js, + mjs, json, png, gif, diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino index 805458ce694..942846ff1a4 100644 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino +++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/WiFiBlueToothSwitch.ino @@ -15,6 +15,11 @@ // Sketch shows how to switch between WiFi and BlueTooth or use both // Button is attached between GPIO 0 and GND and modes are switched with each press +#include "soc/soc_caps.h" +#if !CONFIG_SOC_BT_SUPPORTED +#error "This example requires native Bluetooth support" +#endif + #include "WiFi.h" #define STA_SSID "your-ssid" #define STA_PASS "your-pass" diff --git a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json index f27dd13c83e..5be7c616d24 100644 --- a/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json +++ b/libraries/WiFi/examples/WiFiBlueToothSwitch/ci.json @@ -1,6 +1,6 @@ { "requires": [ - "CONFIG_BT_ENABLED=y" + "CONFIG_SOC_BT_SUPPORTED=y" ], "requires_any": [ "CONFIG_SOC_WIFI_SUPPORTED=y", diff --git a/libraries/WiFi/examples/WiFiUDPClient/udp_server.py b/libraries/WiFi/examples/WiFiUDPClient/udp_server.py index c70a6fe2c37..48ab8f78628 100644 --- a/libraries/WiFi/examples/WiFiUDPClient/udp_server.py +++ b/libraries/WiFi/examples/WiFiUDPClient/udp_server.py @@ -2,6 +2,96 @@ # for messages from the ESP32 board and prints them import socket import sys +import subprocess +import platform + + +def get_interface_ips(): + """Get all available interface IP addresses""" + interface_ips = [] + + # Try using system commands to get interface IPs + system = platform.system().lower() + + try: + if system == "darwin" or system == "linux": + # Use 'ifconfig' on macOS/Linux + result = subprocess.run(["ifconfig"], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + lines = result.stdout.split("\n") + for line in lines: + if "inet " in line and "127.0.0.1" not in line: + # Extract IP address from ifconfig output + parts = line.strip().split() + for i, part in enumerate(parts): + if part == "inet": + if i + 1 < len(parts): + ip = parts[i + 1] + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + break + elif system == "windows": + # Use 'ipconfig' on Windows + result = subprocess.run(["ipconfig"], capture_output=True, text=True, timeout=5) + if result.returncode == 0: + lines = result.stdout.split("\n") + for line in lines: + if "IPv4 Address" in line and "127.0.0.1" not in line: + # Extract IP address from ipconfig output + if ":" in line: + ip = line.split(":")[1].strip() + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + except (subprocess.TimeoutExpired, subprocess.SubprocessError, FileNotFoundError): + print("Error: Failed to get interface IPs using system commands") + print("Trying fallback methods...") + + # Fallback: try to get IPs using socket methods + if not interface_ips: + try: + # Get all IP addresses associated with the hostname + hostname = socket.gethostname() + ip_list = socket.gethostbyname_ex(hostname)[2] + for ip in ip_list: + if ip not in interface_ips and ip != "127.0.0.1": + interface_ips.append(ip) + except socket.gaierror: + print("Error: Failed to get interface IPs using sockets") + + # Fail if no interfaces found + if not interface_ips: + print("Error: No network interfaces found. Please check your network configuration.") + sys.exit(1) + + return interface_ips + + +def select_interface(interface_ips): + """Ask user to select which interface to bind to""" + if len(interface_ips) == 1: + print(f"Using interface: {interface_ips[0]}") + return interface_ips[0] + + print("Multiple network interfaces detected:") + for i, ip in enumerate(interface_ips, 1): + print(f" {i}. {ip}") + + while True: + try: + choice = input(f"Select interface (1-{len(interface_ips)}): ").strip() + choice_idx = int(choice) - 1 + if 0 <= choice_idx < len(interface_ips): + selected_ip = interface_ips[choice_idx] + print(f"Selected interface: {selected_ip}") + return selected_ip + else: + print(f"Please enter a number between 1 and {len(interface_ips)}") + except ValueError: + print("Please enter a valid number") + except KeyboardInterrupt: + print("\nExiting...") + sys.exit(1) + try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -10,15 +100,17 @@ print("Failed to create socket. Error Code : " + str(msg[0]) + " Message " + msg[1]) sys.exit() +# Get available interfaces and let user choose +interface_ips = get_interface_ips() +selected_ip = select_interface(interface_ips) + try: - s.bind(("", 3333)) + s.bind((selected_ip, 3333)) except socket.error as msg: print("Bind failed. Error: " + str(msg[0]) + ": " + msg[1]) sys.exit() -print("Server listening") - -print("Server listening") +print(f"Server listening on {selected_ip}:3333") while 1: d = s.recvfrom(1024) diff --git a/libraries/WiFi/src/WiFi.h b/libraries/WiFi/src/WiFi.h index ea2efd97697..6cfeb1155a0 100644 --- a/libraries/WiFi/src/WiFi.h +++ b/libraries/WiFi/src/WiFi.h @@ -72,6 +72,8 @@ 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 66f8908af77..599402250dd 100644 --- a/libraries/WiFi/src/WiFiGeneric.cpp +++ b/libraries/WiFi/src/WiFiGeneric.cpp @@ -240,100 +240,18 @@ extern "C" void phy_bbpll_en_usb(bool en); #endif #if CONFIG_ESP_WIFI_REMOTE_ENABLED -extern "C" { -//#include "esp_hosted.h" -#include "esp_hosted_transport_config.h" -extern esp_err_t esp_hosted_init(); -extern esp_err_t esp_hosted_deinit(); -}; -typedef struct { - uint8_t pin_clk; - uint8_t pin_cmd; - uint8_t pin_d0; - uint8_t pin_d1; - uint8_t pin_d2; - uint8_t pin_d3; - uint8_t pin_reset; -} sdio_pin_config_t; - -static bool hosted_initialized = false; -static sdio_pin_config_t sdio_pin_config = { -#ifdef BOARD_HAS_SDIO_ESP_HOSTED - .pin_clk = BOARD_SDIO_ESP_HOSTED_CLK, - .pin_cmd = BOARD_SDIO_ESP_HOSTED_CMD, - .pin_d0 = BOARD_SDIO_ESP_HOSTED_D0, - .pin_d1 = BOARD_SDIO_ESP_HOSTED_D1, - .pin_d2 = BOARD_SDIO_ESP_HOSTED_D2, - .pin_d3 = BOARD_SDIO_ESP_HOSTED_D3, - .pin_reset = BOARD_SDIO_ESP_HOSTED_RESET -#else - .pin_clk = CONFIG_ESP_SDIO_PIN_CLK, - .pin_cmd = CONFIG_ESP_SDIO_PIN_CMD, - .pin_d0 = CONFIG_ESP_SDIO_PIN_D0, - .pin_d1 = CONFIG_ESP_SDIO_PIN_D1, - .pin_d2 = CONFIG_ESP_SDIO_PIN_D2, - .pin_d3 = CONFIG_ESP_SDIO_PIN_D3, - .pin_reset = CONFIG_ESP_SDIO_GPIO_RESET_SLAVE -#endif -}; bool WiFiGenericClass::setPins(int8_t clk, int8_t cmd, int8_t d0, int8_t d1, int8_t d2, int8_t d3, int8_t rst) { - if (clk < 0 || cmd < 0 || d0 < 0 || d1 < 0 || d2 < 0 || d3 < 0 || rst < 0) { - log_e("All SDIO pins must be defined"); - return false; - } - if (hosted_initialized) { - log_e("SDIO pins must be set before WiFi is initialized"); - return false; - } - sdio_pin_config.pin_clk = clk; - sdio_pin_config.pin_cmd = cmd; - sdio_pin_config.pin_d0 = d0; - sdio_pin_config.pin_d1 = d1; - sdio_pin_config.pin_d2 = d2; - sdio_pin_config.pin_d3 = d3; - sdio_pin_config.pin_reset = rst; - return true; + return hostedSetPins(clk, cmd, d0, d1, d2, d3, rst); } -static bool wifiHostedInit() { - if (!hosted_initialized) { - hosted_initialized = true; - struct esp_hosted_sdio_config conf = INIT_DEFAULT_HOST_SDIO_CONFIG(); - conf.pin_clk.pin = sdio_pin_config.pin_clk; - conf.pin_cmd.pin = sdio_pin_config.pin_cmd; - conf.pin_d0.pin = sdio_pin_config.pin_d0; - conf.pin_d1.pin = sdio_pin_config.pin_d1; - conf.pin_d2.pin = sdio_pin_config.pin_d2; - conf.pin_d3.pin = sdio_pin_config.pin_d3; - conf.pin_reset.pin = sdio_pin_config.pin_reset; - // esp_hosted_sdio_set_config() will fail on second attempt but here temporarily to not cause exception on reinit - if (esp_hosted_sdio_set_config(&conf) != ESP_OK || esp_hosted_init() != ESP_OK) { - log_e("esp_hosted_init failed!"); - hosted_initialized = false; - return false; - } - log_v("ESP-HOSTED initialized!"); - } - // Attach pins to PeriMan here - // Slave chip model is CONFIG_IDF_SLAVE_TARGET - // sdio_pin_config.pin_clk - // sdio_pin_config.pin_cmd - // sdio_pin_config.pin_d0 - // sdio_pin_config.pin_d1 - // sdio_pin_config.pin_d2 - // sdio_pin_config.pin_d3 - // sdio_pin_config.pin_reset - - return true; -} #endif bool wifiLowLevelInit(bool persistent) { if (!lowLevelInitDone) { lowLevelInitDone = true; #if CONFIG_ESP_WIFI_REMOTE_ENABLED - if (!wifiHostedInit()) { + if (!hostedInitWiFi()) { lowLevelInitDone = false; return lowLevelInitDone; } @@ -402,11 +320,7 @@ static bool wifiLowLevelDeinit() { arduino_event.event_id = ARDUINO_EVENT_WIFI_OFF; Network.postEvent(&arduino_event); #if CONFIG_ESP_WIFI_REMOTE_ENABLED - if (hosted_initialized && esp_hosted_deinit() == ESP_OK) { - hosted_initialized = false; - log_v("ESP-HOSTED uninitialized!"); - // detach SDIO pins from PeriMan - } + hostedDeinitWiFi(); #endif } } diff --git a/libraries/WiFi/src/WiFiScan.cpp b/libraries/WiFi/src/WiFiScan.cpp index 086b875fcb2..b9978d8e0ad 100644 --- a/libraries/WiFi/src/WiFiScan.cpp +++ b/libraries/WiFi/src/WiFiScan.cpp @@ -113,6 +113,9 @@ int16_t * @param status STATUS */ void WiFiScanClass::_scanDone() { + if (!(WiFiGenericClass::getStatusBits() & WIFI_SCANNING_BIT)) { + return; //Ignore if not scanning, scan was started by other + } esp_wifi_scan_get_ap_num(&(WiFiScanClass::_scanCount)); if (WiFiScanClass::_scanResult) { free(WiFiScanClass::_scanResult); diff --git a/libraries/WiFiProv/src/WiFiProv.h b/libraries/WiFiProv/src/WiFiProv.h index d34727b6896..0ebc5956682 100644 --- a/libraries/WiFiProv/src/WiFiProv.h +++ b/libraries/WiFiProv/src/WiFiProv.h @@ -65,6 +65,8 @@ class WiFiProvClass { void printQR(const char *name, const char *pop, const char *transport, Print &out = Serial); }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIFIPROV) extern WiFiProvClass WiFiProv; +#endif #endif /* SOC_WIFI_SUPPORTED */ diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index 9cebdfaa304..91a9ddc44bb 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -142,6 +142,7 @@ class TwoWire : public HardwareI2C { #endif /* SOC_I2C_SUPPORT_SLAVE */ }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIRE) extern TwoWire Wire; #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) #if SOC_I2C_NUM > 1 @@ -154,6 +155,7 @@ extern TwoWire Wire2; extern TwoWire Wire1; #endif /* SOC_HP_I2C_NUM */ #endif +#endif #endif /* SOC_I2C_SUPPORTED */ #endif /* TwoWire_h */ diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/README.md b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md similarity index 78% rename from libraries/Zigbee/examples/Zigbee_Binary_Input/README.md rename to libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md index 6ca3aac7119..18b869446f1 100644 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input/README.md +++ b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/README.md @@ -1,6 +1,6 @@ -# Arduino-ESP32 Zigbee Binary Input Example +# Arduino-ESP32 Zigbee Binary Input Output Example -This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input device with two different applications: HVAC fan status and security zone armed status. +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) binary input/output device with multiple applications: HVAC fan status/control, security zone armed status, and HVAC humidifier control. # Supported Targets @@ -9,12 +9,17 @@ Currently, this example supports the following targets. | Supported Targets | ESP32-C6 | ESP32-H2 | | ----------------- | -------- | -------- | -## Binary Input Functions - - * The example implements two binary inputs: - - HVAC Fan Status: Reports the current state of a fan - - Security Zone Armed: Reports the armed state of a security zone - * By clicking the button (BOOT) on this board, it will toggle both binary inputs and immediately send a report of their states to the network. +## Binary Input/Output Functions + + * The example implements three binary devices: + - **Binary Fan Device (Endpoint 1)**: + - Binary Input: HVAC Fan Status - Reports the current state of a fan + - Binary Output: HVAC Fan - Controls the fan switch with callback function + - **Binary Zone Device (Endpoint 2)**: + - Binary Input: Security Zone Armed - Reports the armed state of a security zone + - **Binary Humidifier Device (Endpoint 3)**: + - Binary Output: HVAC Humidifier - Controls the humidifier switch with callback function + * By clicking the button (BOOT) on this board, it will toggle all binary inputs/outputs and immediately send a report of their states to the network. * Holding the button for more than 3 seconds will trigger a factory reset of the Zigbee device. ## Hardware Required diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino similarity index 67% rename from libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino rename to libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino index de0cf606dcd..60ae2735131 100644 --- a/libraries/Zigbee/examples/Zigbee_Binary_Input/Zigbee_Binary_Input.ino +++ b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/Zigbee_Binary_Input_Output.ino @@ -13,9 +13,9 @@ // limitations under the License. /** - * @brief This example demonstrates Zigbee binary input device. + * @brief This example demonstrates Zigbee binary input/output device. * - * The example demonstrates how to use Zigbee library to create an end device binary sensor device. + * The example demonstrates how to use Zigbee library to create an end device binary sensor/switch device. * * Proper Zigbee mode must be selected in Tools->Zigbee mode * and also the correct partition scheme must be selected in Tools->Partition Scheme. @@ -34,13 +34,24 @@ /* Zigbee binary sensor device configuration */ #define BINARY_DEVICE_ENDPOINT_NUMBER 1 -uint8_t binaryPin = A0; uint8_t button = BOOT_PIN; ZigbeeBinary zbBinaryFan = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER); ZigbeeBinary zbBinaryZone = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 1); +ZigbeeBinary zbBinaryHumidifier = ZigbeeBinary(BINARY_DEVICE_ENDPOINT_NUMBER + 2); -bool binaryStatus = false; +bool zoneStatus = false; + +void fanSwitch(bool state) { + Serial.println("Fan switch changed to: " + String(state)); + // Switch Fan status input signaling the fan status has changed + zbBinaryFan.setBinaryInput(state); + zbBinaryFan.reportBinaryInput(); +} + +void humidifierSwitch(bool state) { + Serial.println("Humidifier switch changed to: " + String(state)); +} void setup() { Serial.begin(115200); @@ -55,19 +66,33 @@ void setup() { // Optional: set Zigbee device name and model zbBinaryFan.setManufacturerAndModel("Espressif", "ZigbeeBinarySensor"); - // Set up binary fan status input (HVAC) + // Set up binary fan status input + switch output (HVAC) zbBinaryFan.addBinaryInput(); zbBinaryFan.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_HVAC_FAN_STATUS); zbBinaryFan.setBinaryInputDescription("Fan Status"); + zbBinaryFan.addBinaryOutput(); + zbBinaryFan.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN); + zbBinaryFan.setBinaryOutputDescription("Fan Switch"); + + zbBinaryFan.onBinaryOutputChange(fanSwitch); + // Set up binary zone armed input (Security) zbBinaryZone.addBinaryInput(); zbBinaryZone.setBinaryInputApplication(BINARY_INPUT_APPLICATION_TYPE_SECURITY_ZONE_ARMED); zbBinaryZone.setBinaryInputDescription("Zone Armed"); + // Set up binary humidifier output (HVAC) + zbBinaryHumidifier.addBinaryOutput(); + zbBinaryHumidifier.setBinaryOutputApplication(BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER); + zbBinaryHumidifier.setBinaryOutputDescription("Humidifier Switch"); + + zbBinaryHumidifier.onBinaryOutputChange(humidifierSwitch); + // Add endpoints to Zigbee Core Zigbee.addEndpoint(&zbBinaryFan); Zigbee.addEndpoint(&zbBinaryZone); + Zigbee.addEndpoint(&zbBinaryHumidifier); Serial.println("Starting Zigbee..."); // When all EPs are registered, start Zigbee in End Device mode @@ -101,12 +126,19 @@ void loop() { Zigbee.factoryReset(); } } - // Toggle binary input - binaryStatus = !binaryStatus; - zbBinaryFan.setBinaryInput(binaryStatus); - zbBinaryZone.setBinaryInput(binaryStatus); - zbBinaryFan.reportBinaryInput(); + + // Toggle fan + zbBinaryFan.setBinaryOutput(!zbBinaryFan.getBinaryOutput()); + zbBinaryFan.reportBinaryOutput(); + + // Toggle zone + zoneStatus = !zoneStatus; + zbBinaryZone.setBinaryInput(zoneStatus); zbBinaryZone.reportBinaryInput(); + + // Toggle humidifier + zbBinaryHumidifier.setBinaryOutput(!zbBinaryHumidifier.getBinaryOutput()); + zbBinaryHumidifier.reportBinaryOutput(); } delay(100); } diff --git a/libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json b/libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.json similarity index 100% rename from libraries/Zigbee/examples/Zigbee_Binary_Input/ci.json rename to libraries/Zigbee/examples/Zigbee_Binary_Input_Output/ci.json diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md new file mode 100644 index 00000000000..662ebe780f7 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/README.md @@ -0,0 +1,90 @@ +# Arduino-ESP32 Zigbee Multistate Input Output Example + +This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) multistate input/output device. + +# Supported Targets + +Currently, this example supports the following targets. + +| Supported Targets | ESP32-C6 | ESP32-H2 | +| ----------------- | -------- | -------- | + +## Multistate Device Functions + +This example demonstrates two different multistate devices: + +1. **Standard Multistate Device** (`zbMultistateDevice`): Uses predefined application states from the Zigbee specification + - Application Type 0: Fan states (Off, On, Auto) + - Application Type 7: Light states (High, Normal, Low) + +2. **Custom Multistate Device** (`zbMultistateDeviceCustom`): Uses user-defined custom states + - Custom fan states: Off, On, UltraSlow, Slow, Fast, SuperFast + +* After this board first starts up, it will be configured as two multistate devices with different state configurations. +* By clicking the button (BOOT) on this board, the devices will cycle through their respective states and report the changes to the network. + +## Hardware Required + +* A USB cable for power supply and programming + +### Configure the Project + +Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2). + +The example creates two multistate devices: +- **Endpoint 1**: Standard multistate device using predefined Zigbee application types +- **Endpoint 2**: Custom multistate device using user-defined states + +You can modify the state names and configurations by changing the following variables: +- `multistate_custom_state_names[]`: Array of custom state names +- Application type and length macros: `ZB_MULTISTATE_APPLICATION_TYPE_X_STATE_NAMES`, +`ZB_MULTISTATE_APPLICATION_TYPE_X_NUM_STATES`, `ZB_MULTISTATE_APPLICATION_TYPE_X_INDEX` +- Device descriptions and application types in the setup() function + +#### Using Arduino IDE + +To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits). + +* Before Compile/Verify, select the correct board: `Tools -> Board`. +* Select the Coordinator/Router Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)` +* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee ZCZR 4MB with spiffs` +* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port. +* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`. + +## Troubleshooting + +If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator. +You can do the following: + +* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`. +* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack. + +By default, the coordinator network is closed after rebooting or flashing new firmware. +To open the network you have 2 options: + +* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`. +* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join. + +***Important: Make sure you are using a good quality USB cable and that you have a reliable power source*** + +* **LED not blinking:** Check the wiring connection and the IO selection. +* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed. +* **COM port not detected:** Check the USB cable and the USB to Serial driver installation. + +If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute). + +## Contribute + +To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst) + +If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome! + +Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else. + +## Resources + +* Official ESP32 Forum: [Link](https://esp32.com) +* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32) +* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf) +* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf) +* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com) diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino new file mode 100644 index 00000000000..d0f944e5e23 --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/Zigbee_Multistate_Input_Output.ino @@ -0,0 +1,189 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @brief This example demonstrates Zigbee multistate input / output device. + * + * The example demonstrates how to use Zigbee library to create a router multistate device. + * In the example, we have two multistate devices: + * - zbMultistateDevice: uses defined application states from Zigbee specification + * - zbMultistateDeviceCustom: uses custom application states (user defined) + * + * Proper Zigbee mode must be selected in Tools->Zigbee mode + * and also the correct partition scheme must be selected in Tools->Partition Scheme. + * + * Please check the README.md for instructions and more detailed description. + * + * NOTE: HomeAssistant ZHA does not support multistate input and output clusters yet. + * + * Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/) + */ + +#ifndef ZIGBEE_MODE_ZCZR +#error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode" +#endif + +#include "Zigbee.h" + +/* Zigbee multistate device configuration */ +#define MULTISTATE_DEVICE_ENDPOINT_NUMBER 1 + +uint8_t button = BOOT_PIN; + +// zbMultistateDevice will use defined application states +ZigbeeMultistate zbMultistateDevice = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER); + +// zbMultistateDeviceCustom will use custom application states (user defined) +ZigbeeMultistate zbMultistateDeviceCustom = ZigbeeMultistate(MULTISTATE_DEVICE_ENDPOINT_NUMBER + 1); + +const char *multistate_custom_state_names[6] = {"Off", "On", "UltraSlow", "Slow", "Fast", "SuperFast"}; + +void onStateChange(uint16_t state) { + // print the state + Serial.printf("Received state change: %d\r\n", state); + // print the state name using the stored state names + const char *const *state_names = ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES; + if (state_names && state < zbMultistateDevice.getMultistateOutputStateNamesLength()) { + Serial.printf("State name: %s\r\n", state_names[state]); + } + // print state index of possible options + Serial.printf("State index: %d / %d\r\n", state, zbMultistateDevice.getMultistateOutputStateNamesLength() - 1); +} + +void onStateChangeCustom(uint16_t state) { + // print the state + Serial.printf("Received state change: %d\r\n", state); + // print the state name using the stored state names + if (state < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength()) { + Serial.printf("State name: %s\r\n", multistate_custom_state_names[state]); + } + // print state index of possible options + Serial.printf("State index: %d / %d\r\n", state, zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1); + + Serial.print("Changing to fan mode to: "); + switch (state) { + case 0: Serial.println("Off"); break; + case 1: Serial.println("On"); break; + case 2: Serial.println("UltraSlow"); break; + case 3: Serial.println("Slow"); break; + case 4: Serial.println("Fast"); break; + case 5: Serial.println("SuperFast"); break; + default: Serial.println("Invalid state"); break; + } +} + +void setup() { + log_d("Starting serial"); + Serial.begin(115200); + + // Init button switch + log_d("Init button switch"); + pinMode(button, INPUT_PULLUP); + + // Optional: set Zigbee device name and model + log_d("Set Zigbee device name and model"); + zbMultistateDevice.setManufacturerAndModel("Espressif", "ZigbeeMultistateDevice"); + + // Set up analog input + log_d("Add Multistate Input"); + zbMultistateDevice.addMultistateInput(); + log_d("Set Multistate Input Application"); + zbMultistateDevice.setMultistateInputApplication(ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX); + log_d("Set Multistate Input Description"); + zbMultistateDevice.setMultistateInputDescription("Fan (on/off/auto)"); + zbMultistateDevice.setMultistateInputStates(ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES); + + // Set up analog output + log_d("Add Multistate Output"); + zbMultistateDevice.addMultistateOutput(); + log_d("Set Multistate Output Application"); + zbMultistateDevice.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX); + log_d("Set Multistate Output Description"); + zbMultistateDevice.setMultistateOutputDescription("Light (high/normal/low)"); + zbMultistateDevice.setMultistateOutputStates(ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES); + + // Set up custom output + log_d("Add Multistate Output"); + zbMultistateDeviceCustom.addMultistateOutput(); + log_d("Set Multistate Output Application"); + zbMultistateDeviceCustom.setMultistateOutputApplication(ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX); + log_d("Set Multistate Output Description"); + zbMultistateDeviceCustom.setMultistateOutputDescription("Fan (on/off/slow/medium/fast)"); + zbMultistateDeviceCustom.setMultistateOutputStates(5); + + // Set callback function for multistate output change + log_d("Set callback function for multistate output change"); + zbMultistateDevice.onMultistateOutputChange(onStateChange); + zbMultistateDeviceCustom.onMultistateOutputChange(onStateChangeCustom); + + // Add endpoints to Zigbee Core + log_d("Add endpoints to Zigbee Core"); + Zigbee.addEndpoint(&zbMultistateDevice); + Zigbee.addEndpoint(&zbMultistateDeviceCustom); + + Serial.println("Starting Zigbee..."); + // When all EPs are registered, start Zigbee in Router Device mode + if (!Zigbee.begin(ZIGBEE_ROUTER)) { + Serial.println("Zigbee failed to start!"); + Serial.println("Rebooting..."); + ESP.restart(); + } else { + Serial.println("Zigbee started successfully!"); + } + Serial.println("Connecting to network"); + while (!Zigbee.connected()) { + Serial.print("."); + delay(100); + } + Serial.println("Connected"); +} + +void loop() { + // Checking button for factory reset and reporting + if (digitalRead(button) == LOW) { // Push button pressed + // Key debounce handling + delay(100); + int startTime = millis(); + while (digitalRead(button) == LOW) { + delay(50); + if ((millis() - startTime) > 3000) { + // If key pressed for more than 3secs, factory reset Zigbee and reboot + Serial.println("Resetting Zigbee to factory and rebooting in 1s."); + delay(1000); + Zigbee.factoryReset(); + } + } + // For demonstration purposes, increment the multistate output/input value by 1 + if (zbMultistateDevice.getMultistateOutput() < zbMultistateDevice.getMultistateOutputStateNamesLength() - 1) { + zbMultistateDevice.setMultistateOutput(zbMultistateDevice.getMultistateOutput() + 1); + zbMultistateDevice.reportMultistateOutput(); + zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput()); + zbMultistateDevice.reportMultistateInput(); + } else { + zbMultistateDevice.setMultistateOutput(0); + zbMultistateDevice.reportMultistateOutput(); + zbMultistateDevice.setMultistateInput(zbMultistateDevice.getMultistateOutput()); + zbMultistateDevice.reportMultistateInput(); + } + + if (zbMultistateDeviceCustom.getMultistateOutput() < zbMultistateDeviceCustom.getMultistateOutputStateNamesLength() - 1) { + zbMultistateDeviceCustom.setMultistateOutput(zbMultistateDeviceCustom.getMultistateOutput() + 1); + zbMultistateDeviceCustom.reportMultistateOutput(); + } else { + zbMultistateDeviceCustom.setMultistateOutput(0); + zbMultistateDeviceCustom.reportMultistateOutput(); + } + } + delay(100); +} diff --git a/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.json b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.json new file mode 100644 index 00000000000..15d6190e4ae --- /dev/null +++ b/libraries/Zigbee/examples/Zigbee_Multistate_Input_Output/ci.json @@ -0,0 +1,6 @@ +{ + "fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr", + "requires": [ + "CONFIG_ZB_ENABLED=y" + ] +} diff --git a/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino index 29d114014b4..6ea2329a459 100644 --- a/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino +++ b/libraries/Zigbee/examples/Zigbee_OTA_Client/Zigbee_OTA_Client.ino @@ -44,6 +44,18 @@ uint8_t button = BOOT_PIN; ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT); +volatile bool otaRunning = false; + +/********************* Callbacks *************************/ +void otaActiveCallback(bool otaActive) { + otaRunning = otaActive; + if (otaActive) { + Serial.println("OTA started"); + } else { + Serial.println("OTA finished"); + } +} + /********************* RGB LED functions **************************/ void setLED(bool value) { digitalWrite(led, value); @@ -69,6 +81,9 @@ void setup() { // Add OTA client to the light bulb zbLight.addOTAClient(OTA_UPGRADE_RUNNING_FILE_VERSION, OTA_UPGRADE_DOWNLOADED_FILE_VERSION, OTA_UPGRADE_HW_VERSION); + // Optional: Register callback for OTA state change + zbLight.onOTAStateChange(otaActiveCallback); + // Add endpoint to Zigbee Core Serial.println("Adding ZigbeeLight endpoint to Zigbee Core"); Zigbee.addEndpoint(&zbLight); @@ -99,6 +114,10 @@ void loop() { while (digitalRead(button) == LOW) { delay(50); if ((millis() - startTime) > 3000) { + if (otaRunning) { + Serial.println("OTA in progress, cannot reset now"); + break; + } // If key pressed for more than 3secs, factory reset Zigbee and reboot Serial.println("Resetting Zigbee to factory and rebooting in 1s."); delay(1000); diff --git a/libraries/Zigbee/keywords.txt b/libraries/Zigbee/keywords.txt index 68721c1a66f..67cb47ee319 100644 --- a/libraries/Zigbee/keywords.txt +++ b/libraries/Zigbee/keywords.txt @@ -26,6 +26,7 @@ ZigbeeFlowSensor KEYWORD1 ZigbeeGateway KEYWORD1 ZigbeeIlluminanceSensor KEYWORD1 ZigbeeLight KEYWORD1 +ZigbeeMultistate KEYWORD1 ZigbeeOccupancySensor KEYWORD1 ZigbeePM25Sensor KEYWORD1 ZigbeePowerOutlet KEYWORD1 @@ -55,6 +56,8 @@ zb_cmd_type_t KEYWORD1 # ZigbeeCore begin KEYWORD2 +start KEYWORD2 +stop KEYWORD2 started KEYWORD2 connected KEYWORD2 getRole KEYWORD2 @@ -73,6 +76,7 @@ scanComplete KEYWORD2 getScanResult KEYWORD2 scanDelete KEYWORD2 factoryReset KEYWORD2 +allowMultiEndpointBinding KEYWORD2 # Common ZigbeeEP setEpConfig KEYWORD2 @@ -224,6 +228,27 @@ getFanMode KEYWORD2 getFanModeSequence KEYWORD2 onFanModeChange KEYWORD2 +# ZigbeeMultistate +addMultistateInput KEYWORD2 +addMultistateOutput KEYWORD2 +onMultistateOutputChange KEYWORD2 +setMultistateInput KEYWORD2 +getMultistateInput KEYWORD2 +setMultistateOutput KEYWORD2 +getMultistateOutput KEYWORD2 +reportMultistateInput KEYWORD2 +reportMultistateOutput KEYWORD2 +setMultistateInputApplication KEYWORD2 +setMultistateInputDescription KEYWORD2 +setMultistateInputStates KEYWORD2 +setMultistateOutputApplication KEYWORD2 +setMultistateOutputDescription KEYWORD2 +setMultistateOutputStates KEYWORD2 +#getMultistateInputStateNames KEYWORD2 +getMultistateInputStateNamesLength KEYWORD2 +#getMultistateOutputStateNames KEYWORD2 +getMultistateOutputStateNamesLength KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### @@ -238,3 +263,42 @@ ZIGBEE_DEFAULT_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_UART_RCP_RADIO_CONFIG LITERAL1 ZIGBEE_DEFAULT_HOST_CONFIG LITERAL1 ZB_ARRAY_LENGHT LITERAL1 + +# ZigbeeMultistate +ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES LITERAL1 +ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX LITERAL1 diff --git a/libraries/Zigbee/src/Zigbee.h b/libraries/Zigbee/src/Zigbee.h index ab94a163f3e..837c19fa461 100644 --- a/libraries/Zigbee/src/Zigbee.h +++ b/libraries/Zigbee/src/Zigbee.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Zigbee library header file for includes of all Zigbee library headers. #pragma once @@ -31,6 +45,7 @@ #include "ep/ZigbeeElectricalMeasurement.h" #include "ep/ZigbeeFlowSensor.h" #include "ep/ZigbeeIlluminanceSensor.h" +#include "ep/ZigbeeMultistate.h" #include "ep/ZigbeeOccupancySensor.h" #include "ep/ZigbeePM25Sensor.h" #include "ep/ZigbeePressureSensor.h" diff --git a/libraries/Zigbee/src/ZigbeeCore.cpp b/libraries/Zigbee/src/ZigbeeCore.cpp index 90b29cf9d0a..9706d732752 100644 --- a/libraries/Zigbee/src/ZigbeeCore.cpp +++ b/libraries/Zigbee/src/ZigbeeCore.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee Core Functions */ #include "ZigbeeCore.h" @@ -32,6 +46,7 @@ ZigbeeCore::ZigbeeCore() { _scan_duration = 3; // default scan duration _rx_on_when_idle = true; _debug = false; + _allow_multi_endpoint_binding = false; _global_default_response_cb = nullptr; // Initialize global callback to nullptr if (!lock) { lock = xSemaphoreCreateBinary(); @@ -378,7 +393,9 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } @@ -408,11 +425,13 @@ void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) { break; } } - log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); if (!found) { + log_d("Device not bound to endpoint %d and it is free to bound!", (*it)->getEndpoint()); (*it)->findEndpoint(&cmd_req); log_d("Endpoint %d is searching for device", (*it)->getEndpoint()); - break; // Only one endpoint per device + if (!Zigbee.allowMultiEndpointBinding()) { // If multi endpoint binding is not allowed, break the loop to keep backwards compatibility + break; + } } } } @@ -515,8 +534,14 @@ void ZigbeeCore::scanNetworks(u_int32_t channel_mask, u_int8_t scan_duration) { log_e("Zigbee stack is not started, cannot scan networks"); return; } + if (_scan_status == ZB_SCAN_RUNNING) { + log_w("Scan already in progress, ignoring new scan request"); + return; + } log_v("Scanning Zigbee networks"); + esp_zb_lock_acquire(portMAX_DELAY); esp_zb_zdo_active_scan_request(channel_mask, scan_duration, scanCompleteCallback); + esp_zb_lock_release(); _scan_status = ZB_SCAN_RUNNING; } @@ -750,6 +775,24 @@ void ZigbeeCore::setNVRAMChannelMask(uint32_t mask) { log_v("Channel mask set to 0x%08x", mask); } +void ZigbeeCore::stop() { + if (started()) { + vTaskSuspend(xTaskGetHandle("Zigbee_main")); + log_v("Zigbee stack stopped"); + _started = false; + } + return; +} + +void ZigbeeCore::start() { + if (!started()) { + vTaskResume(xTaskGetHandle("Zigbee_main")); + log_v("Zigbee stack started"); + _started = true; + } + return; +} + // Function to convert enum value to string const char *ZigbeeCore::getDeviceTypeString(esp_zb_ha_standard_devices_t deviceId) { switch (deviceId) { diff --git a/libraries/Zigbee/src/ZigbeeCore.h b/libraries/Zigbee/src/ZigbeeCore.h index df334e1620d..6295832fa03 100644 --- a/libraries/Zigbee/src/ZigbeeCore.h +++ b/libraries/Zigbee/src/ZigbeeCore.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee core class */ #pragma once @@ -103,6 +117,7 @@ class ZigbeeCore { zigbee_scan_result_t *_scan_result; SemaphoreHandle_t lock; bool _debug; + bool _allow_multi_endpoint_binding; // Global default response callback void (*_global_default_response_cb)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster); @@ -124,6 +139,8 @@ class ZigbeeCore { bool begin(zigbee_role_t role = ZIGBEE_END_DEVICE, bool erase_nvs = false); bool begin(esp_zb_cfg_t *role_cfg, bool erase_nvs = false); // bool end(); + void stop(); + void start(); bool started() { return _started; @@ -180,6 +197,13 @@ class ZigbeeCore { return _debug; } + void allowMultiEndpointBinding(bool allow) { + _allow_multi_endpoint_binding = allow; + } + bool allowMultiEndpointBinding() { + return _allow_multi_endpoint_binding; + } + // Set global default response callback void onGlobalDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster)) { _global_default_response_cb = callback; @@ -206,6 +230,8 @@ class ZigbeeCore { } }; +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_ZIGBEE) extern ZigbeeCore Zigbee; +#endif #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ZigbeeEP.cpp b/libraries/Zigbee/src/ZigbeeEP.cpp index 6b63cae0312..b857eb5e7a5 100644 --- a/libraries/Zigbee/src/ZigbeeEP.cpp +++ b/libraries/Zigbee/src/ZigbeeEP.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Common Class for Zigbee End Point */ #include "ZigbeeEP.h" @@ -14,6 +28,7 @@ ZigbeeEP::ZigbeeEP(uint8_t endpoint) { _ep_config.endpoint = 0; _cluster_list = nullptr; _on_identify = nullptr; + _on_ota_state_change = nullptr; _read_model = NULL; _read_manufacturer = NULL; _time_status = 0; @@ -304,6 +319,12 @@ void ZigbeeEP::zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message) { } } +void ZigbeeEP::zbOTAState(bool otaActive) { + if (_on_ota_state_change != NULL) { + _on_ota_state_change(otaActive); + } +} + bool ZigbeeEP::addTimeCluster(tm time, int32_t gmt_offset) { time_t utc_time = 0; // Check if time is set diff --git a/libraries/Zigbee/src/ZigbeeEP.h b/libraries/Zigbee/src/ZigbeeEP.h index 23217407003..3a7dea19a41 100644 --- a/libraries/Zigbee/src/ZigbeeEP.h +++ b/libraries/Zigbee/src/ZigbeeEP.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Common Class for Zigbee End point */ #pragma once @@ -135,8 +149,11 @@ class ZigbeeEP { // list of all handlers function calls, to be override by EPs implementation virtual void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) {}; virtual void zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; + virtual void + zbWriteAttributeResponse(uint16_t cluster_id, uint16_t attribute_id, esp_zb_zcl_status_t status, uint8_t src_endpoint, esp_zb_zcl_addr_t src_address) {}; virtual void zbReadBasicCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIdentify(const esp_zb_zcl_set_attr_value_message_t *message); + virtual void zbOTAState(bool otaActive); virtual void zbWindowCoveringMovementCmd(const esp_zb_zcl_window_covering_movement_message_t *message) {}; virtual void zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute); //already implemented virtual void zbIASZoneStatusChangeNotification(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message) {}; @@ -160,6 +177,10 @@ class ZigbeeEP { _on_identify = callback; } + void onOTAStateChange(void (*callback)(bool state)) { + _on_ota_state_change = callback; + } + void onDefaultResponse(void (*callback)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status)) { _on_default_response = callback; } @@ -170,6 +191,7 @@ class ZigbeeEP { char *_read_manufacturer; char *_read_model; void (*_on_identify)(uint16_t time); + void (*_on_ota_state_change)(bool state); void (*_on_default_response)(zb_cmd_type_t resp_to_cmd, esp_zb_zcl_status_t status); time_t _read_time; int32_t _read_timezone; diff --git a/libraries/Zigbee/src/ZigbeeHandlers.cpp b/libraries/Zigbee/src/ZigbeeHandlers.cpp index 0986056dcd9..9b574ea7422 100644 --- a/libraries/Zigbee/src/ZigbeeHandlers.cpp +++ b/libraries/Zigbee/src/ZigbeeHandlers.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Zigbee Common Functions */ #include "ZigbeeCore.h" #include "Arduino.h" @@ -28,6 +42,7 @@ static bool s_tagid_received = false; static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message); static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message); static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_resp_message_t *message); +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message); static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message); static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone_status_change_notification_message_t *message); static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message); @@ -58,8 +73,9 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID: ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message); break; - case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; - default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; + case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break; + case ESP_ZB_CORE_CMD_WRITE_ATTR_RESP_CB_ID: ret = zb_cmd_write_attr_resp_handler((esp_zb_zcl_cmd_write_attr_resp_message_t *)message); break; + default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break; } return ret; } @@ -156,6 +172,36 @@ static esp_err_t zb_cmd_read_attr_resp_handler(const esp_zb_zcl_cmd_read_attr_re return ESP_OK; } +static esp_err_t zb_cmd_write_attr_resp_handler(const esp_zb_zcl_cmd_write_attr_resp_message_t *message) { + if (!message) { + log_e("Empty message"); + return ESP_FAIL; + } + if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Received message: error status(%d)", message->info.status); + return ESP_ERR_INVALID_ARG; + } + log_v( + "Write attribute response: from address(0x%x) src endpoint(%d) to dst endpoint(%d) cluster(0x%x)", message->info.src_address.u.short_addr, + message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster + ); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + if (message->info.dst_endpoint == (*it)->getEndpoint()) { + esp_zb_zcl_write_attr_resp_variable_t *variable = message->variables; + while (variable) { + log_v("Write attribute response: status(%d), cluster(0x%x), attribute(0x%x)", variable->status, message->info.cluster, variable->attribute_id); + if (variable->status == ESP_ZB_ZCL_STATUS_SUCCESS) { + (*it)->zbWriteAttributeResponse( + message->info.cluster, variable->attribute_id, variable->status, message->info.src_endpoint, message->info.src_address + ); + } + variable = variable->next; + } + } + } + return ESP_OK; +} + static esp_err_t zb_configure_report_resp_handler(const esp_zb_zcl_cmd_config_report_resp_message_t *message) { if (!message) { log_e("Empty message"); @@ -292,6 +338,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu switch (message->upgrade_status) { case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_START: log_i("Zigbee - OTA upgrade start"); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(true); // Notify that OTA is active + } start_time = esp_timer_get_time(); s_ota_partition = esp_ota_get_next_update_partition(NULL); assert(s_ota_partition); @@ -302,6 +351,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu #endif if (ret != ESP_OK) { log_e("Zigbee - Failed to begin OTA partition, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } break; @@ -315,6 +367,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu ret = esp_element_ota_data(total_size, message->payload, message->payload_size, &payload, &payload_size); if (ret != ESP_OK) { log_e("Zigbee - Failed to element OTA data, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } #if CONFIG_ZB_DELTA_OTA @@ -324,6 +379,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu #endif if (ret != ESP_OK) { log_e("Zigbee - Failed to write OTA data to partition, status: %s", esp_err_to_name(ret)); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } return ret; } } @@ -343,6 +401,9 @@ static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_valu message->ota_header.file_version, message->ota_header.manufacturer_code, message->ota_header.image_type, message->ota_header.image_size, (esp_timer_get_time() - start_time) / 1000 ); + for (std::list::iterator it = Zigbee.ep_objects.begin(); it != Zigbee.ep_objects.end(); ++it) { + (*it)->zbOTAState(false); // Notify that OTA is no longer active + } #if CONFIG_ZB_DELTA_OTA ret = esp_delta_ota_end(s_ota_handle); #else diff --git a/libraries/Zigbee/src/ZigbeeTypes.h b/libraries/Zigbee/src/ZigbeeTypes.h index 5025f90db7c..5b5a34fcb5b 100644 --- a/libraries/Zigbee/src/ZigbeeTypes.h +++ b/libraries/Zigbee/src/ZigbeeTypes.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #pragma once #include "esp_zigbee_core.h" diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp index 309739d54c9..cc0222fac42 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeAnalog.h" #if CONFIG_ZB_ENABLED #include diff --git a/libraries/Zigbee/src/ep/ZigbeeAnalog.h b/libraries/Zigbee/src/ep/ZigbeeAnalog.h index 5218a0b7d60..38a267ad27c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeAnalog.h +++ b/libraries/Zigbee/src/ep/ZigbeeAnalog.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Analog sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeBinary.cpp b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp index aa37ceb3020..ff99b56091b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeBinary.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeBinary.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeBinary.h" #if CONFIG_ZB_ENABLED @@ -41,6 +55,36 @@ bool ZigbeeBinary::addBinaryInput() { return true; } +bool ZigbeeBinary::addBinaryOutput() { + esp_zb_attribute_list_t *esp_zb_binary_output_cluster = esp_zb_binary_output_cluster_create(NULL); + + // Create default description for Binary Output + char default_description[] = "\x0D" + "Binary Output"; + uint32_t application_type = 0x00000000 | (0x04 << 24); // Group ID 0x04 + + esp_err_t ret = + esp_zb_binary_output_cluster_add_attr(esp_zb_binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_binary_output_cluster_add_attr(esp_zb_binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_cluster_list_add_binary_output_cluster(_cluster_list, esp_zb_binary_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Binary Output cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + _binary_clusters |= BINARY_OUTPUT; + return true; +} + // Check Zigbee Cluster Specification 3.14.11.19.4 Binary Inputs (BI) Types for application type values bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) { if (!(_binary_clusters & BINARY_INPUT)) { @@ -61,6 +105,26 @@ bool ZigbeeBinary::setBinaryInputApplication(uint32_t application_type) { return true; } +// Check Zigbee Cluster Specification 3.14.11.19.5 Binary Outputs (BO) Types for application type values +bool ZigbeeBinary::setBinaryOutputApplication(uint32_t application_type) { + if (!(_binary_clusters & BINARY_OUTPUT)) { + log_e("Binary Output cluster not added"); + return false; + } + + // Add the Binary Output group ID (0x04) to the application type + uint32_t application_type_value = (0x04 << 24) | application_type; + + esp_zb_attribute_list_t *binary_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Binary Output application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + bool ZigbeeBinary::setBinaryInput(bool input) { esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; if (!(_binary_clusters & BINARY_INPUT)) { @@ -141,4 +205,107 @@ bool ZigbeeBinary::setBinaryInputDescription(const char *description) { return true; } +bool ZigbeeBinary::setBinaryOutputDescription(const char *description) { + if (!(_binary_clusters & BINARY_OUTPUT)) { + log_e("Binary Output cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the binary output cluster + esp_zb_attribute_list_t *binary_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (binary_output_cluster == nullptr) { + log_e("Failed to get binary output cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(binary_output_cluster, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +//set attribute method -> method overridden in child class +void ZigbeeBinary::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) { + _output_state = *(bool *)message->attribute.data.value; + binaryOutputChanged(); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for Binary Output", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Binary endpoint", message->info.cluster); + } +} + +void ZigbeeBinary::binaryOutputChanged() { + if (_on_binary_output_change) { + _on_binary_output_change(_output_state); + } else { + log_w("No callback function set for binary output change"); + } +} + +bool ZigbeeBinary::setBinaryOutput(bool output) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _output_state = output; + binaryOutputChanged(); + + log_v("Updating binary output to %d", output); + /* Update binary output */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID, &_output_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set binary output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeBinary::reportBinaryOutput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Binary Output report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Binary Output report sent"); + return true; +} + #endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeBinary.h b/libraries/Zigbee/src/ep/ZigbeeBinary.h index 5a543604970..c0ca3c8b92f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeBinary.h +++ b/libraries/Zigbee/src/ep/ZigbeeBinary.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Binary sensor endpoint inherited from common EP class */ #pragma once @@ -38,10 +52,26 @@ enum zigbee_binary_clusters { #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_HEAT_DETECTION 0x01000008 // Type 0x01, Index 0x0008 #define BINARY_INPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF +// HVAC application types for Binary Output (more can be found in Zigbee Cluster Specification 3.14.11.19.5 Binary Outputs (BO) Types) +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_BOILER 0x00000003 // Type 0x00, Index 0x0003 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_CHILLER 0x0000000D // Type 0x00, Index 0x000D +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_FAN 0x00000022 // Type 0x00, Index 0x0022 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HEATING_VALVE 0x0000002C // Type 0x00, Index 0x002C +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_HUMIDIFIER 0x00000033 // Type 0x00, Index 0x0033 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_PREHEAT 0x00000034 // Type 0x00, Index 0x0034 +#define BINARY_OUTPUT_APPLICATION_TYPE_HVAC_OTHER 0x0000FFFF // Type 0x00, Index 0xFFFF + +// Security application types for Binary Output +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ARM_DISARM_COMMAND 0x01000000 // Type 0x01, Index 0x0000 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OCCUPANCY_CONTROL 0x01000001 // Type 0x01, Index 0x0001 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ENABLE_CONTROL 0x01000002 // Type 0x01, Index 0x0002 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_ACCESS_CONTROL 0x01000003 // Type 0x01, Index 0x0003 +#define BINARY_OUTPUT_APPLICATION_TYPE_SECURITY_OTHER 0x0100FFFF // Type 0x01, Index 0xFFFF + typedef struct zigbee_binary_cfg_s { esp_zb_basic_cluster_cfg_t basic_cfg; esp_zb_identify_cluster_cfg_t identify_cfg; - // esp_zb_binary_output_cluster_cfg_t binary_output_cfg; + esp_zb_binary_output_cluster_cfg_t binary_output_cfg; esp_zb_binary_input_cluster_cfg_t binary_input_cfg; } zigbee_binary_cfg_t; @@ -52,33 +82,41 @@ class ZigbeeBinary : public ZigbeeEP { // Add binary cluster bool addBinaryInput(); - // bool addBinaryOutput(); + bool addBinaryOutput(); // Set the application type and description for the binary input bool setBinaryInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_input.h for application type values bool setBinaryInputDescription(const char *description); // Set the application type and description for the binary output - // bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values - // bool setBinaryOutputDescription(const char *description); + bool setBinaryOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_binary_output.h for application type values + bool setBinaryOutputDescription(const char *description); // Use to set a cb function to be called on binary output change - // void onBinaryOutputChange(void (*callback)(bool binary_output)) { - // _on_binary_output_change = callback; - // } + void onBinaryOutputChange(void (*callback)(bool binary_output)) { + _on_binary_output_change = callback; + } - // Set the binary input value + // Set the binary input/output value bool setBinaryInput(bool input); + bool setBinaryOutput(bool output); + + // Get the Binary Output value + bool getBinaryOutput() { + return _output_state; + } - // Report Binary Input value + // Report Binary Input/Output value bool reportBinaryInput(); + bool reportBinaryOutput(); private: - // void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; - // void (*_on_binary_output_change)(bool); - // void binaryOutputChanged(bool binary_output); + void (*_on_binary_output_change)(bool); + void binaryOutputChanged(); + bool _output_state; uint8_t _binary_clusters; }; diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp index 2b8271f09a9..225bb620f0b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeCarbonDioxideSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h index e0a6de48648..bd64e50e51e 100644 --- a/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeCarbonDioxideSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp index 3611c232c20..3d1b6a57d1b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include #include "ZigbeeColorDimmableLight.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h index 6681f213ad0..5f47002d196 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmableLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp index f795ed1a3c8..1d18d3c10e8 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeColorDimmerSwitch.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h index ada1f75fbb4..9d813c47651 100644 --- a/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeColorDimmerSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Switch endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp index ced8e43d6ea..4142f87fe16 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeContactSwitch.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h index b33effd8dfc..b3be38c3eb4 100644 --- a/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeContactSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp index 05a7e5ad6c1..70e565e32c7 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeDimmableLight.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h index 747fdbafaef..da1d1067fad 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeDimmableLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp index c5b62ee2b75..2ca032b01e5 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeDoorWindowHandle.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h index efffd34b12f..d4a2d81eb39 100644 --- a/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h +++ b/libraries/Zigbee/src/ep/ZigbeeDoorWindowHandle.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee door window handle (IAS Zone) endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp index 8fa456c967a..6a862bade98 100644 --- a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeElectricalMeasurement.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h index 74d4a718fe2..9cace1c4b6c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h +++ b/libraries/Zigbee/src/ep/ZigbeeElectricalMeasurement.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp index f4b32ce1200..45724eb915c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFanControl.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeFanControl.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeFanControl.h b/libraries/Zigbee/src/ep/ZigbeeFanControl.h index 25b5862c5c4..75d614e0ed4 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFanControl.h +++ b/libraries/Zigbee/src/ep/ZigbeeFanControl.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp index 8a60af5a8e1..ddaee3ea88f 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeFlowSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h index fa16b4a5636..0402dd15377 100644 --- a/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeFlowSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Flow sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeGateway.cpp b/libraries/Zigbee/src/ep/ZigbeeGateway.cpp index b0be81395ca..2640efc4e60 100644 --- a/libraries/Zigbee/src/ep/ZigbeeGateway.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeGateway.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeGateway.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeGateway.h b/libraries/Zigbee/src/ep/ZigbeeGateway.h index 3925630c0b8..e5ec3c05eb0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeGateway.h +++ b/libraries/Zigbee/src/ep/ZigbeeGateway.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Gateway endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp index f1661c3a026..a2a6d364f9d 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeIlluminanceSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h index 133dfc315db..fdc369b4a55 100644 --- a/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeIlluminanceSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Illuminance sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.cpp b/libraries/Zigbee/src/ep/ZigbeeLight.cpp index edfac04fcdf..75cfacd8418 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeLight.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeLight.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeLight.h b/libraries/Zigbee/src/ep/ZigbeeLight.h index 773fbb14ec5..ce88a7a9aad 100644 --- a/libraries/Zigbee/src/ep/ZigbeeLight.h +++ b/libraries/Zigbee/src/ep/ZigbeeLight.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Light endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp b/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp new file mode 100644 index 00000000000..5c289192023 --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeMultistate.cpp @@ -0,0 +1,455 @@ +#include "ZigbeeMultistate.h" +#if CONFIG_ZB_ENABLED + +// Workaround for ESP-ZIGBEE-SDK 1.6.6 known issue +#ifdef __cplusplus +extern "C" { +#endif +extern void esp_zb_zcl_multi_input_init_server(void); +extern void esp_zb_zcl_multi_input_init_client(void); + +void esp_zb_zcl_multistate_input_init_server(void) { + esp_zb_zcl_multi_input_init_server(); +} +void esp_zb_zcl_multistate_input_init_client(void) { + esp_zb_zcl_multi_input_init_client(); +} +#ifdef __cplusplus +} +#endif + +ZigbeeMultistate::ZigbeeMultistate(uint8_t endpoint) : ZigbeeEP(endpoint) { + _device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID; + + //Create basic multistate clusters without configuration + _cluster_list = esp_zb_zcl_cluster_list_create(); + esp_zb_cluster_list_add_basic_cluster(_cluster_list, esp_zb_basic_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + + _ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_SIMPLE_SENSOR_DEVICE_ID, .app_device_version = 0}; + + // Initialize member variables + _multistate_clusters = 0; + _input_state = 0; + _output_state = 0; + _input_state_names_length = 0; + _output_state_names_length = 0; + // _input_state_names = nullptr; + // _output_state_names = nullptr; + _on_multistate_output_change = nullptr; +} + +bool ZigbeeMultistate::addMultistateInput() { + esp_zb_multistate_input_cluster_cfg_t multistate_input_cfg = { + .number_of_states = 3, .out_of_service = false, .present_value = 0, .status_flags = ESP_ZB_ZCL_MULTI_VALUE_STATUS_FLAGS_NORMAL + }; + + esp_zb_attribute_list_t *multistate_input_cluster = esp_zb_multistate_input_cluster_create(&multistate_input_cfg); + + // Create default description for Multistate Input + char default_description[] = "\x10" // Size of the description text + "Multistate Input"; // Description text + uint32_t application_type = 0x00000000 | (0x0D << 24); // Application type + // const char* state_text[] = { "Off", "On", "Auto" }; // State text array + + esp_err_t ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // ret = esp_zb_multistate_input_cluster_add_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_STATE_TEXT_ID, (void *)state_text); + // if (ret != ESP_OK) { + // log_e("Failed to add state text attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + // return false; + // } + + ret = esp_zb_cluster_list_add_multistate_input_cluster(_cluster_list, multistate_input_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Multistate Input cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _multistate_clusters |= MULTISTATE_INPUT; + return true; +} + +bool ZigbeeMultistate::addMultistateOutput() { + esp_zb_multistate_output_cluster_cfg_t multistate_output_cfg = { + .number_of_states = 3, .out_of_service = false, .present_value = 0, .status_flags = ESP_ZB_ZCL_MULTI_VALUE_STATUS_FLAGS_NORMAL + }; + + esp_zb_attribute_list_t *multistate_output_cluster = esp_zb_multistate_output_cluster_create(&multistate_output_cfg); + + // Create default description for Multistate Output + char default_description[] = "\x11" // Size of the description text + "Multistate Output"; // Description text + uint32_t application_type = 0x00000000 | (0x0E << 24); + // const char* state_text[] = { "Off", "On", "Auto" }; // State text array + + esp_err_t ret = + esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_DESCRIPTION_ID, (void *)default_description); + if (ret != ESP_OK) { + log_e("Failed to add description attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + ret = esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type); + if (ret != ESP_OK) { + log_e("Failed to add application type attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // ret = esp_zb_multistate_output_cluster_add_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_STATE_TEXT_ID, (void *)state_text); + // if (ret != ESP_OK) { + // log_e("Failed to add state text attribute: 0x%x: %s", ret, esp_err_to_name(ret)); + // return false; + // } + + ret = esp_zb_cluster_list_add_multistate_output_cluster(_cluster_list, multistate_output_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (ret != ESP_OK) { + log_e("Failed to add Multistate Output cluster: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _multistate_clusters |= MULTISTATE_OUTPUT; + return true; +} + +bool ZigbeeMultistate::setMultistateInputApplication(uint32_t application_type) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + // Add the Multistate Input group ID (0x0D) to the application type + uint32_t application_type_value = (0x0D << 24) | application_type; + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Multistate Input application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutputApplication(uint32_t application_type) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + // Add the Multistate Output group ID (0x0E) to the application type + uint32_t application_type_value = (0x0E << 24) | application_type; + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_APPLICATION_TYPE_ID, (void *)&application_type_value); + if (ret != ESP_OK) { + log_e("Failed to set Multistate Output application type: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateInputDescription(const char *description) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the multistate input cluster + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutputDescription(const char *description) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + // Allocate a new array of size length + 2 (1 for the length, 1 for null terminator) + char zb_description[ZB_MAX_NAME_LENGTH + 2]; + + // Convert description to ZCL string + size_t description_length = strlen(description); + if (description_length > ZB_MAX_NAME_LENGTH) { + log_e("Description is too long"); + return false; + } + + // Get and check the multistate output cluster + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + // Store the length as the first element + zb_description[0] = static_cast(description_length); // Cast size_t to char + // Use memcpy to copy the characters to the result array + memcpy(zb_description + 1, description, description_length); + // Null-terminate the array + zb_description[description_length + 1] = '\0'; + + // Update the description attribute + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_DESCRIPTION_ID, (void *)zb_description); + if (ret != ESP_OK) { + log_e("Failed to set description: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateInputStates(uint16_t number_of_states) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID, (void *)&number_of_states); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _input_state_names_length = number_of_states; + return true; +} + +bool ZigbeeMultistate::setMultistateOutputStates(uint16_t number_of_states) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_NUMBER_OF_STATES_ID, (void *)&number_of_states); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + _output_state_names_length = number_of_states; + return true; +} + +/* TODO: revisit this after arrays are supported + +bool ZigbeeMultistate::setMultistateInputStates(const char * const states[], uint16_t states_length) { + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_input_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_input_cluster == nullptr) { + log_e("Failed to get multistate input cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_STATE_TEXT_ID, (void *)states); + if (ret != ESP_OK) { + log_e("Failed to set states text: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_update_attr(multistate_input_cluster, ESP_ZB_ZCL_ATTR_MULTI_INPUT_NUMBER_OF_STATES_ID, (void *)&states_length); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // Store state names locally for getter access + _input_state_names = states; + _input_state_names_length = states_length; + return true; +} + +bool ZigbeeMultistate::setMultistateOutputStates(const char * const states[], uint16_t states_length) { + if (!(_multistate_clusters & MULTISTATE_OUTPUT)) { + log_e("Multistate Output cluster not added"); + return false; + } + + esp_zb_attribute_list_t *multistate_output_cluster = + esp_zb_cluster_list_get_cluster(_cluster_list, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE); + if (multistate_output_cluster == nullptr) { + log_e("Failed to get multistate output cluster"); + return false; + } + + esp_err_t ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_STATE_TEXT_ID, (void *)states); + if (ret != ESP_OK) { + log_e("Failed to set states text: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + ret = esp_zb_cluster_update_attr(multistate_output_cluster, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_NUMBER_OF_STATES_ID, (void *)&states_length); + if (ret != ESP_OK) { + log_e("Failed to set number of states: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + + // Store state names locally for getter access + _output_state_names = states; + _output_state_names_length = states_length; + return true; +} +*/ + +//set attribute method -> method overridden in child class +void ZigbeeMultistate::zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) { + if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT) { + if (message->attribute.id == ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) { + _output_state = *(uint16_t *)message->attribute.data.value; + multistateOutputChanged(); + } else { + log_w("Received message ignored. Attribute ID: %d not supported for Multistate Output", message->attribute.id); + } + } else { + log_w("Received message ignored. Cluster ID: %d not supported for Multistate endpoint", message->info.cluster); + } +} + +void ZigbeeMultistate::multistateOutputChanged() { + if (_on_multistate_output_change) { + _on_multistate_output_change(_output_state); + } else { + log_w("No callback function set for multistate output change"); + } +} + +bool ZigbeeMultistate::setMultistateInput(uint16_t state) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + if (!(_multistate_clusters & MULTISTATE_INPUT)) { + log_e("Multistate Input cluster not added"); + return false; + } + log_d("Setting multistate input to %d", state); + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID, &state, false + ); + esp_zb_lock_release(); + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set multistate input: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::setMultistateOutput(uint16_t state) { + esp_zb_zcl_status_t ret = ESP_ZB_ZCL_STATUS_SUCCESS; + _output_state = state; + multistateOutputChanged(); + + log_v("Updating multistate output to %d", state); + /* Update multistate output */ + esp_zb_lock_acquire(portMAX_DELAY); + ret = esp_zb_zcl_set_attribute_val( + _endpoint, ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE, ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID, &_output_state, false + ); + esp_zb_lock_release(); + + if (ret != ESP_ZB_ZCL_STATUS_SUCCESS) { + log_e("Failed to set multistate output: 0x%x: %s", ret, esp_zb_zcl_status_to_name(ret)); + return false; + } + return true; +} + +bool ZigbeeMultistate::reportMultistateInput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_MULTI_INPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_MULTI_INPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Multistate Input report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Multistate Input report sent"); + return true; +} + +bool ZigbeeMultistate::reportMultistateOutput() { + /* Send report attributes command */ + esp_zb_zcl_report_attr_cmd_t report_attr_cmd; + report_attr_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT; + report_attr_cmd.attributeID = ESP_ZB_ZCL_ATTR_MULTI_OUTPUT_PRESENT_VALUE_ID; + report_attr_cmd.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_CLI; + report_attr_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_MULTI_OUTPUT; + report_attr_cmd.zcl_basic_cmd.src_endpoint = _endpoint; + report_attr_cmd.manuf_code = ESP_ZB_ZCL_ATTR_NON_MANUFACTURER_SPECIFIC; + + esp_zb_lock_acquire(portMAX_DELAY); + esp_err_t ret = esp_zb_zcl_report_attr_cmd_req(&report_attr_cmd); + esp_zb_lock_release(); + if (ret != ESP_OK) { + log_e("Failed to send Multistate Output report: 0x%x: %s", ret, esp_err_to_name(ret)); + return false; + } + log_v("Multistate Output report sent"); + return true; +} + +#endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeMultistate.h b/libraries/Zigbee/src/ep/ZigbeeMultistate.h new file mode 100644 index 00000000000..e4eb0be267c --- /dev/null +++ b/libraries/Zigbee/src/ep/ZigbeeMultistate.h @@ -0,0 +1,188 @@ +/* Class of Zigbee Multistate sensor endpoint inherited from common EP class */ + +#pragma once + +#include "soc/soc_caps.h" +#include "sdkconfig.h" +#if CONFIG_ZB_ENABLED + +#include "ZigbeeEP.h" +#include "ha/esp_zigbee_ha_standard.h" + +// Types for Multistate Input/Output +// uint16_t for present value -> index to array of states +// uint16_t for number of states +// uint8_t chars for state names, with max of 16 chars ASCII + +// Multistate Input/Output Application Types Defines +#define ZB_MULTISTATE_APPLICATION_TYPE_0_INDEX 0x0000 +#define ZB_MULTISTATE_APPLICATION_TYPE_0_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_0_STATE_NAMES \ + (const char *const[]) { \ + "Off", "On", "Auto" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_1_INDEX 0x0001 +#define ZB_MULTISTATE_APPLICATION_TYPE_1_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_1_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Low", "Medium", "High" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_2_INDEX 0x0002 +#define ZB_MULTISTATE_APPLICATION_TYPE_2_NUM_STATES 7 +#define ZB_MULTISTATE_APPLICATION_TYPE_2_STATE_NAMES \ + (const char *const[]) { \ + "Auto", "Heat", "Cool", "Off", "Emergency Heat", "Fan Only", "Max Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_3_INDEX 0x0003 +#define ZB_MULTISTATE_APPLICATION_TYPE_3_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_3_STATE_NAMES \ + (const char *const[]) { \ + "Occupied", "Unoccupied", "Standby", "Bypass" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_4_INDEX 0x0004 +#define ZB_MULTISTATE_APPLICATION_TYPE_4_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_4_STATE_NAMES \ + (const char *const[]) { \ + "Inactive", "Active", "Hold" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_5_INDEX 0x0005 +#define ZB_MULTISTATE_APPLICATION_TYPE_5_NUM_STATES 8 +#define ZB_MULTISTATE_APPLICATION_TYPE_5_STATE_NAMES \ + (const char *const[]) { \ + "Auto", "Warm-up", "Water Flush", "Autocalibration", "Shutdown Open", "Shutdown Closed", "Low Limit", "Test and Balance" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_6_INDEX 0x0006 +#define ZB_MULTISTATE_APPLICATION_TYPE_6_NUM_STATES 6 +#define ZB_MULTISTATE_APPLICATION_TYPE_6_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Auto", "Heat Cool", "Heat Only", "Cool Only", "Fan Only" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_7_INDEX 0x0007 +#define ZB_MULTISTATE_APPLICATION_TYPE_7_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_7_STATE_NAMES \ + (const char *const[]) { \ + "High", "Normal", "Low" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_8_INDEX 0x0008 +#define ZB_MULTISTATE_APPLICATION_TYPE_8_NUM_STATES 4 +#define ZB_MULTISTATE_APPLICATION_TYPE_8_STATE_NAMES \ + (const char *const[]) { \ + "Occupied", "Unoccupied", "Startup", "Shutdown" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_9_INDEX 0x0009 +#define ZB_MULTISTATE_APPLICATION_TYPE_9_NUM_STATES 3 +#define ZB_MULTISTATE_APPLICATION_TYPE_9_STATE_NAMES \ + (const char *const[]) { \ + "Night", "Day", "Hold" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_10_INDEX 0x000A +#define ZB_MULTISTATE_APPLICATION_TYPE_10_NUM_STATES 5 +#define ZB_MULTISTATE_APPLICATION_TYPE_10_STATE_NAMES \ + (const char *const[]) { \ + "Off", "Cool", "Heat", "Auto", "Emergency Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_11_INDEX 0x000B +#define ZB_MULTISTATE_APPLICATION_TYPE_11_NUM_STATES 7 +#define ZB_MULTISTATE_APPLICATION_TYPE_11_STATE_NAMES \ + (const char *const[]) { \ + "Shutdown Closed", "Shutdown Open", "Satisfied", "Mixing", "Cooling", "Heating", "Suppl Heat" \ + } + +#define ZB_MULTISTATE_APPLICATION_TYPE_OTHER_INDEX 0xFFFF + +//enum for bits set to check what multistate cluster were added +enum zigbee_multistate_clusters { + MULTISTATE_INPUT = 1, + MULTISTATE_OUTPUT = 2 +}; + +typedef struct zigbee_multistate_cfg_s { + esp_zb_basic_cluster_cfg_t basic_cfg; + esp_zb_identify_cluster_cfg_t identify_cfg; +} zigbee_multistate_cfg_t; + +class ZigbeeMultistate : public ZigbeeEP { +public: + ZigbeeMultistate(uint8_t endpoint); + ~ZigbeeMultistate() {} + + // Add multistate clusters + bool addMultistateInput(); + bool addMultistateOutput(); + + // Set the application type and description for the multistate input + bool setMultistateInputApplication(uint32_t application_type); // Check esp_zigbee_zcl_multistate_input.h for application type values + bool setMultistateInputDescription(const char *description); + bool setMultistateInputStates(uint16_t number_of_states); + // bool setMultistateInputStates(const char * const states[], uint16_t states_length); + + // Set the application type and description for the multistate output + bool setMultistateOutputApplication(uint32_t application_type); // Check esp_zigbee_zcl_multistate_output.h for application type values + bool setMultistateOutputDescription(const char *description); + bool setMultistateOutputStates(uint16_t number_of_states); + // bool setMultistateOutputStates(const char * const states[], uint16_t states_length); + + // Use to set a cb function to be called on multistate output change + void onMultistateOutputChange(void (*callback)(uint16_t state)) { + _on_multistate_output_change = callback; + } + + // Set the Multistate Input/Output value + bool setMultistateInput(uint16_t state); + bool setMultistateOutput(uint16_t state); + + // Get the Multistate Input/Output values + uint16_t getMultistateInput() { + return _input_state; + } + uint16_t getMultistateOutput() { + return _output_state; + } + + // Get state names and length + uint16_t getMultistateInputStateNamesLength() { + return _input_state_names_length; + } + uint16_t getMultistateOutputStateNamesLength() { + return _output_state_names_length; + } + // const char* const* getMultistateInputStateNames() { + // return _input_state_names; + // } + // const char* const* getMultistateOutputStateNames() { + // return _output_state_names; + // } + + // Report Multistate Input/Output + bool reportMultistateInput(); + bool reportMultistateOutput(); + +private: + void zbAttributeSet(const esp_zb_zcl_set_attr_value_message_t *message) override; + + void (*_on_multistate_output_change)(uint16_t state); + void multistateOutputChanged(); + + uint8_t _multistate_clusters; + uint16_t _output_state; + uint16_t _input_state; + + // Local storage for state names + uint16_t _input_state_names_length; + uint16_t _output_state_names_length; + // const char* const* _input_state_names; + // const char* const* _output_state_names; +}; + +#endif // CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp index b8f88fed4a4..959fffdbbc3 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeOccupancySensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h index 7408e10a76b..9f1b190a18a 100644 --- a/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeOccupancySensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp index d25d15e5de3..fc2ca820c9a 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePM25Sensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h index 344f3e1f479..e4b47574704 100644 --- a/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePM25Sensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee PM2.5 sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp index b96ba672c04..9009c1dd1a3 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePowerOutlet.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h index 1ddb19587c7..231f7b6d3b9 100644 --- a/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h +++ b/libraries/Zigbee/src/ep/ZigbeePowerOutlet.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Power outlet endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp index bca06a35d0c..4fdf8600fbe 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeePressureSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h index f93df7a7411..cd38a24d4a0 100644 --- a/libraries/Zigbee/src/ep/ZigbeePressureSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeePressureSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Pressure sensor endpoint inherited from common EP class */ #pragma once @@ -24,8 +38,8 @@ .pressure_meas_cfg = \ { \ .measured_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_VALUE_DEFAULT_VALUE, \ - .min_value = ESP_ZB_ZCL_PATTR_RESSURE_MEASUREMENT_MIN_VALUE_DEFAULT_VALUE, \ - .max_value = ESP_ZB_ZCL_PATTR_RESSURE_MEASUREMENT_MAX_VALUE_DEFAULT_VALUE, \ + .min_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MIN_VALUE_DEFAULT_VALUE, \ + .max_value = ESP_ZB_ZCL_ATTR_PRESSURE_MEASUREMENT_MAX_VALUE_DEFAULT_VALUE, \ }, \ } // clang-format on diff --git a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp index 20db20d758a..b12cde268c0 100644 --- a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeRangeExtender.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h index f9e4a963164..8d4c1566415 100644 --- a/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h +++ b/libraries/Zigbee/src/ep/ZigbeeRangeExtender.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Range Extender endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp index 68e7a7430cc..d3e3aae4cf7 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeSwitch.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeSwitch.h b/libraries/Zigbee/src/ep/ZigbeeSwitch.h index 0e7011c3312..f449843d85c 100644 --- a/libraries/Zigbee/src/ep/ZigbeeSwitch.h +++ b/libraries/Zigbee/src/ep/ZigbeeSwitch.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee On/Off Switch endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp index 7126dae15cf..9159464b309 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeTempSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h index bc769b32de6..e7219c17335 100644 --- a/libraries/Zigbee/src/ep/ZigbeeTempSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeTempSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Temperature + Humidity sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp index f3bfd7d981b..b08c256ab6b 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeThermostat.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeThermostat.h b/libraries/Zigbee/src/ep/ZigbeeThermostat.h index c10a9d7f974..1f6aef485d6 100644 --- a/libraries/Zigbee/src/ep/ZigbeeThermostat.h +++ b/libraries/Zigbee/src/ep/ZigbeeThermostat.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Temperature sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp index 6be457c389a..218638ed3cb 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeVibrationSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h index 2f67c7bb6b4..9757257b5c1 100644 --- a/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeVibrationSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee contact switch (IAS Zone) endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp index 72184927d4d..672b6254017 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeWindSpeedSensor.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h index 641c1d84780..b5371f8fdef 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindSpeedSensor.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee WindSpeed sensor endpoint inherited from common EP class */ #pragma once diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp index 7c7889dbbf7..a51db92ccc9 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.cpp @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "ZigbeeWindowCovering.h" #if CONFIG_ZB_ENABLED diff --git a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h index 288d92c5765..f63e5f9e642 100644 --- a/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h +++ b/libraries/Zigbee/src/ep/ZigbeeWindowCovering.h @@ -1,3 +1,17 @@ +// Copyright 2025 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + /* Class of Zigbee Window Covering endpoint inherited from common EP class */ #pragma once diff --git a/package/package_esp32_index.template.json b/package/package_esp32_index.template.json index 3909c833c56..7382af86779 100644 --- a/package/package_esp32_index.template.json +++ b/package/package_esp32_index.template.json @@ -6,7 +6,7 @@ "websiteURL": "https://github.com/espressif/arduino-esp32", "email": "hristo@espressif.com", "help": { - "online": "http://esp32.com" + "online": "https://esp32.com" }, "platforms": [ { @@ -51,7 +51,7 @@ { "packager": "esp32", "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-b66b5448-v1" + "version": "idf-release_v5.5-129cd0d2-v4" }, { "packager": "esp32", @@ -76,12 +76,12 @@ { "packager": "esp32", "name": "openocd-esp32", - "version": "v0.12.0-esp32-20250422" + "version": "v0.12.0-esp32-20250707" }, { "packager": "esp32", "name": "esptool_py", - "version": "5.0.0" + "version": "5.1.0" }, { "packager": "esp32", @@ -91,7 +91,7 @@ { "packager": "esp32", "name": "mklittlefs", - "version": "3.0.0-gnu12-dc7f933" + "version": "4.0.2-db0513a" }, { "packager": "arduino", @@ -104,63 +104,63 @@ "tools": [ { "name": "esp32-arduino-libs", - "version": "idf-release_v5.5-b66b5448-v1", + "version": "idf-release_v5.5-129cd0d2-v4", "systems": [ { "host": "i686-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "i686-pc-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-b66b5448-v1.zip", - "checksum": "SHA-256:a871d945c6bfb685ecff5e30ad759f280c841ea143071466b2e611bd1800f18f", - "size": "430471837" + "url": "https://github.com/espressif/esp32-arduino-lib-builder/releases/download/idf-release_v5.5/esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "archiveFileName": "esp32-arduino-libs-idf-release_v5.5-129cd0d2-v4.zip", + "checksum": "SHA-256:be9402929176a556007fea48db7bb79da431614a2e153c54f92c8314f6ccd099", + "size": "446164326" } ] }, @@ -414,166 +414,166 @@ }, { "name": "openocd-esp32", - "version": "v0.12.0-esp32-20250422", + "version": "v0.12.0-esp32-20250707", "systems": [ { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-amd64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-amd64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:eb1fa9b21c65b45a2200af6dcc2914e32335d37b6dbbd181778dcc0dc025e70a", - "size": "2445546" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-amd64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-amd64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:766293bd7a08900d3536f87a0a7ade960f07266f16e4147f95ca5ce4e15d4c5d", + "size": "2489724" }, { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-arm64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-arm64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:f70334a9b12a75b4d943e09fa5db30973037c39dbb54d6fa9f1a7118228b3d1c", - "size": "2330926" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-arm64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-arm64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:34b6883c372444b49950893b2fc0101aefd10d404a88ef72c97e80199f8544d3", + "size": "2371243" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-linux-armel-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-linux-armel-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:4ac34d6fd1af86aeda87c8318732f8d691c300c285c7fd2f5037c432c63fbbb3", - "size": "2470732" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-linux-armel-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-linux-armel-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:fd48492cf3ee16577c661fdccc14c349d34a9ab93aac5039ddf72332d4f4b70b", + "size": "2517680" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-macos-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-macos-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:9186a7a06304c6d9201cbce4ee3c7099b393bf8d329cda17a68874f92308f6ce", - "size": "2548730" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-macos-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-macos-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:6267be53892a76d535938a1b044b685adc7d292f090447e8a3e3d0f0996474d1", + "size": "2585348" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-macos-arm64-0.12.0-esp32-20250422.tar.gz", - "archiveFileName": "openocd-esp32-macos-arm64-0.12.0-esp32-20250422.tar.gz", - "checksum": "SHA-256:2cc39318d52f393233ff1f777871aebe5b97b3fbad29556a238489263401b774", - "size": "2593819" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-macos-arm64-0.12.0-esp32-20250707.tar.gz", + "archiveFileName": "openocd-esp32-macos-arm64-0.12.0-esp32-20250707.tar.gz", + "checksum": "SHA-256:150e938ac48a6ee031ddbc8b31043bc7f2073ab2ee4896b658918d35899673c3", + "size": "2628741" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-win32-0.12.0-esp32-20250422.zip", - "archiveFileName": "openocd-esp32-win32-0.12.0-esp32-20250422.zip", - "checksum": "SHA-256:ecb4f8533fa9098d10000f5f7e8b8eaa8591015b824b481078ddb2b37e7aa6f2", - "size": "2988859" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-win32-0.12.0-esp32-20250707.zip", + "archiveFileName": "openocd-esp32-win32-0.12.0-esp32-20250707.zip", + "checksum": "SHA-256:666274b04af7f36b430b6d063006051c37b8635b5175735ad5af07a1fbc6f486", + "size": "3034680" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250422/openocd-esp32-win64-0.12.0-esp32-20250422.zip", - "archiveFileName": "openocd-esp32-win64-0.12.0-esp32-20250422.zip", - "checksum": "SHA-256:e9eae8e1a8d0e030cd81dcb08394a9137cb7338a6211dfabcdbdfb37b58c5a23", - "size": "2988858" + "url": "https://github.com/espressif/openocd-esp32/releases/download/v0.12.0-esp32-20250707/openocd-esp32-win64-0.12.0-esp32-20250707.zip", + "archiveFileName": "openocd-esp32-win64-0.12.0-esp32-20250707.zip", + "checksum": "SHA-256:5186ba3f7ee29fb6ab68a4ed7bb417211bad76ecdcdf9280a9187ebfd549a3c1", + "size": "3034680" } ] }, { "name": "esptool_py", - "version": "5.0.0", + "version": "5.1.0", "systems": [ { "host": "aarch64-linux-gnu", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-linux-aarch64.tar.gz", - "archiveFileName": "esptool-v5.0.0-linux-aarch64.tar.gz", - "checksum": "SHA-256:2bf239f3ed76141a957cadb205b94414ec6da9ace4e85f285e247d20a92b83e3", - "size": "58231895" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-aarch64.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-aarch64.tar.gz", + "checksum": "SHA-256:d2b60d4570cd4919b87eddcbeaab2e0411548b5aab865c234aed8ecc8e5403ac", + "size": "56292242" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-linux-amd64.tar.gz", - "archiveFileName": "esptool-v5.0.0-linux-amd64.tar.gz", - "checksum": "SHA-256:3b3835d266ac61f3242758f2fe34e3b33dbe6ee4b5acde005da793356f9f7043", - "size": "100783748" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-amd64.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-amd64.tar.gz", + "checksum": "SHA-256:49d572d50f6b1f089d1d81d3bd3bd357fbcc40f4f8fd4874f2dc51ad534abb01", + "size": "57690602" }, { "host": "arm-linux-gnueabihf", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-linux-armv7.tar.gz", - "archiveFileName": "esptool-v5.0.0-linux-armv7.tar.gz", - "checksum": "SHA-256:e55cd321abecfcf27f72a2bff5d5e19a5365fd400de66d71c5e7218e77556315", - "size": "53461760" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-linux-armv7.tar.gz", + "archiveFileName": "esptool-v5.1.0-linux-armv7.tar.gz", + "checksum": "SHA-256:e22ecb0293fe73c80d0a5fd05873f9ea49a68985b16991cf5980d2b90c8c7276", + "size": "53396928" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-macos-amd64.tar.gz", - "archiveFileName": "esptool-v5.0.0-macos-amd64.tar.gz", - "checksum": "SHA-256:424da2bdf0435257ad81bcb7eae6fd8dd7f675ce5b2ee60032f4ecec4d6a5d45", - "size": "59629533" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-macos-amd64.tar.gz", + "archiveFileName": "esptool-v5.1.0-macos-amd64.tar.gz", + "checksum": "SHA-256:c485511e0906cb1e0277c5eecff1c4a77b89d76d0c940b685dc9fce2fad4b242", + "size": "59088390" }, { "host": "arm64-apple-darwin", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-macos-arm64.tar.gz", - "archiveFileName": "esptool-v5.0.0-macos-arm64.tar.gz", - "checksum": "SHA-256:b91dfe1da7b0041376683dec10a91dfb266fbda2fb86ed87c4a034ff7182ee56", - "size": "56343104" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-macos-arm64.tar.gz", + "archiveFileName": "esptool-v5.1.0-macos-arm64.tar.gz", + "checksum": "SHA-256:5d5aab5b64b10dc5001cfa96b5bfa48393ae561e6d797c41a1fdd3f5d3843d03", + "size": "56092727" }, { "host": "x86_64-mingw32", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-windows-amd64.zip", - "archiveFileName": "esptool-v5.0.0-windows-amd64.zip", - "checksum": "SHA-256:2294107f66db6f09b886b337728a981173c9e7eab45a030928a8a5a1370611ca", - "size": "59105322" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-windows-amd64.zip", + "archiveFileName": "esptool-v5.1.0-windows-amd64.zip", + "checksum": "SHA-256:f68a8f7728adfc59cd60f9424928199e76eac66372c7bdc23898aa32753a437a", + "size": "59936293" }, { "host": "i686-mingw32", - "url": "https://github.com/espressif/esptool/releases/download/v5.0.0/esptool-v5.0.0-windows-amd64.zip", - "archiveFileName": "esptool-v5.0.0-windows-amd64.zip", - "checksum": "SHA-256:2294107f66db6f09b886b337728a981173c9e7eab45a030928a8a5a1370611ca", - "size": "59105322" + "url": "https://github.com/espressif/esptool/releases/download/v5.1.0/esptool-v5.1.0-windows-amd64.zip", + "archiveFileName": "esptool-v5.1.0-windows-amd64.zip", + "checksum": "SHA-256:f68a8f7728adfc59cd60f9424928199e76eac66372c7bdc23898aa32753a437a", + "size": "59936293" } ] }, { - "version": "3.0.0-gnu12-dc7f933", + "version": "4.0.2-db0513a", "name": "mklittlefs", "systems": [ { - "host": "aarch64-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "aarch64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:fc56e389383749e4cf4fab0fcf75cc0ebc41e59383caf6c2eff1c3d9794af200", - "size": "44651" + "host": "aarch64-apple-darwin", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/aarch64-apple-darwin-mklittlefs-db0513a.tar.gz", + "archiveFileName": "aarch64-apple-darwin-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:9a271456c9caf9129a8881b1d10474dcb15d25ce243dff557fdf4cfaae74f1e4", + "size": "53793" }, { - "host": "arm-linux-gnueabihf", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "arm-linux-gnueabihf.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:52b642dd0545eb3bd8dfb75dde6601df21700e4867763fd2696274be279294c5", - "size": "37211" + "host": "aarch64-linux-gnu", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/aarch64-linux-gnu-mklittlefs-db0513a.tar.gz", + "archiveFileName": "aarch64-linux-gnu-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:281e951ccaf9f637142198bde1ca97be9c330a5ad3b15e9096016a521d459341", + "size": "52890" }, { - "host": "i686-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "i686-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:7886051d8ccc54aed0af2e7cdf6ff992bb51638df86f3b545955697720b6d062", - "size": "48033" + "host": "arm-linux-gnueabihf", + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/arm-linux-gnueabihf-mklittlefs-db0513a.tar.gz", + "archiveFileName": "arm-linux-gnueabihf-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:49e4bba4ef6ff338c301a5d264badd86f3b494e48ea278e8d06a435b5da04f81", + "size": "45403" }, { "host": "i686-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "archiveFileName": "i686-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "checksum": "SHA-256:43740db30ce451454f2337331f10ab4ed41bd83dbf0fa0cb4387107388b59f42", - "size": "332655" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/i686-w64-mingw32-mklittlefs-db0513a.zip", + "archiveFileName": "i686-w64-mingw32-mklittlefs-db0513a.zip", + "checksum": "SHA-256:47d7d9b70397ee8dd0b858d3cc0f01db4d8a03a031652f788d81700c74f107cd", + "size": "489405" }, { "host": "x86_64-apple-darwin", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "x86_64-apple-darwin14.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:e3edd5e05b70db3c7df6b9d626558348ad04804022fe955c799aeb51808c7dc3", - "size": "362608" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-apple-darwin-mklittlefs-db0513a.tar.gz", + "archiveFileName": "x86_64-apple-darwin-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:90f20aa1c5f2d91bbe00821943ef5a291160ce4bc9077cc76f6350b47d978755", + "size": "58977" }, { "host": "x86_64-pc-linux-gnu", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "archiveFileName": "x86_64-linux-gnu.mklittlefs-c41e51a.200706.tar.gz", - "checksum": "SHA-256:66e84dda0aad747517da3785125e05738a540948aab2b7eaa02855167a1eea53", - "size": "46778" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-linux-gnu-mklittlefs-db0513a.tar.gz", + "archiveFileName": "x86_64-linux-gnu-mklittlefs-db0513a.tar.gz", + "checksum": "SHA-256:7a70428b7089bf1c9d481b0e070e99cd8a430d37e197b7d3db64f24ba8891508", + "size": "54000" }, { "host": "x86_64-mingw32", - "url": "https://github.com/earlephilhower/esp-quick-toolchain/releases/download/3.0.0-gnu12/x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "archiveFileName": "x86_64-w64-mingw32.mklittlefs-c41e51a.200706.zip", - "checksum": "SHA-256:2e319077491f8e832e96eb4f2f7a70dd919333cee4b388c394e0e848d031d542", - "size": "345132" + "url": "https://github.com/earlephilhower/mklittlefs/releases/download/4.0.2/x86_64-w64-mingw32-mklittlefs-db0513a.zip", + "archiveFileName": "x86_64-w64-mingw32-mklittlefs-db0513a.zip", + "checksum": "SHA-256:e99dbfcf2b808a2020254764f06e336aa6a4d253ab09bcabe01399fcd95d9ab8", + "size": "452707" } ] }, diff --git a/tests/validation/touch/ci.json b/tests/validation/touch/ci.json index 855e9bd964d..3ccb5dfabe8 100644 --- a/tests/validation/touch/ci.json +++ b/tests/validation/touch/ci.json @@ -1,4 +1,5 @@ { + "fqbn_append": "DebugLevel=verbose", "platforms": { "qemu": false, "wokwi": false diff --git a/tests/validation/touch/touch.ino b/tests/validation/touch/touch.ino index 97aac8a65e6..21ed6119c02 100644 --- a/tests/validation/touch/touch.ino +++ b/tests/validation/touch/touch.ino @@ -1,35 +1,30 @@ #include #include "soc/soc_caps.h" -#include "driver/touch_pad.h" - -#if SOC_TOUCH_SENSOR_VERSION == 3 #include "hal/touch_sensor_ll.h" -#endif #if CONFIG_IDF_TARGET_ESP32 -#define TEST_TOUCH_CHANNEL (9) +#define TEST_TOUCH_CHANNEL (7) static touch_pad_t touch_list[TEST_TOUCH_CHANNEL] = { TOUCH_PAD_NUM0, //TOUCH_PAD_NUM1 is GPIO0, for download. - TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, TOUCH_PAD_NUM8, TOUCH_PAD_NUM9 + TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7 /*,TOUCH_PAD_NUM8, TOUCH_PAD_NUM9*/ }; -uint8_t TOUCH_GPIOS[] = {4, 2, 15, 13, 12, 14, 27, 33, 32}; +uint8_t TOUCH_GPIOS[] = {4, /* 0,*/ 2, 15, 13, 12, 14, 27 /*, 33, 32*/}; #define NO_TOUCH_GPIO 25 #elif (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3) -#define TEST_TOUCH_CHANNEL (12) //14 +#define TEST_TOUCH_CHANNEL (8) //14 static touch_pad_t touch_list[TEST_TOUCH_CHANNEL] = { - TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, - TOUCH_PAD_NUM8, TOUCH_PAD_NUM9, TOUCH_PAD_NUM10, TOUCH_PAD_NUM11, TOUCH_PAD_NUM12 - //TOUCH_PAD_NUM13, //Wrong reading - //TOUCH_PAD_NUM14 + TOUCH_PAD_NUM1, TOUCH_PAD_NUM2, TOUCH_PAD_NUM3, TOUCH_PAD_NUM4, TOUCH_PAD_NUM5, + TOUCH_PAD_NUM6, TOUCH_PAD_NUM7, TOUCH_PAD_NUM8 + /*TOUCH_PAD_NUM9, TOUCH_PAD_NUM10, TOUCH_PAD_NUM11, TOUCH_PAD_NUM12, TOUCH_PAD_NUM13, TOUCH_PAD_NUM14*/ }; -uint8_t TOUCH_GPIOS[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 /*,13,14*/}; +uint8_t TOUCH_GPIOS[] = {1, 2, 3, 4, 5, 6, 7, 8 /*, 9, 10, 11, 12, 13, 14*/}; #define NO_TOUCH_GPIO 17 @@ -47,21 +42,12 @@ uint8_t TOUCH_GPIOS[] = {2, 3, 4, 5, 6 /*, 7, 8, 9, 10, 11, 12 ,13, 14, 15*/}; #define NO_TOUCH_GPIO 17 #endif -#if CONFIG_IDF_TARGET_ESP32 -#define RELEASED_VALUE 75 //75+ read value to pass test -#define PRESSED_VALUE 20 //20- read value to pass test -#define INTERRUPT_THRESHOLD 40 -#elif CONFIG_IDF_TARGET_ESP32S2 -#define RELEASED_VALUE 10000 //10000- read value to pass test -#define PRESSED_VALUE 42000 //40000+ read value to pass test -#define INTERRUPT_THRESHOLD 30000 -#elif CONFIG_IDF_TARGET_ESP32S3 -#define RELEASED_VALUE 25000 //25000- read value to pass test -#define PRESSED_VALUE 90000 //90000+ read value to pass test -#define INTERRUPT_THRESHOLD 80000 -#elif CONFIG_IDF_TARGET_ESP32P4 -#define PRESSED_VALUE_DIFFERENCE 200 //200+ read value difference against the unpressed value -#define INTERRUPT_THRESHOLD 0 // Use benchmarked threshold +#define INTERRUPT_THRESHOLD 0 // Use benchmarked threshold + +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32P4 +#define PRESSED_VALUE_DIFFERENCE 200 //-200 for ESP32 and +200 for ESP32P4 read value difference against the unpressed value +#elif CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2 +#define PRESSED_VALUE_DIFFERENCE 2000 //2000+ read value difference against the unpressed value #else #error Test not currently supported on this chip. Please adjust and try again! #endif @@ -82,7 +68,7 @@ void gotTouch2() { */ static void test_press_fake(touch_pad_t pad_num) { #if SOC_TOUCH_SENSOR_VERSION <= 2 - touch_pad_set_cnt_mode(pad_num, TOUCH_PAD_SLOPE_1, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_ll_set_charge_speed(pad_num, TOUCH_CHARGE_SPEED_4); #else touch_ll_set_internal_capacitor(0x7f); #endif @@ -93,7 +79,7 @@ static void test_press_fake(touch_pad_t pad_num) { */ static void test_release_fake(touch_pad_t pad_num) { #if SOC_TOUCH_SENSOR_VERSION <= 2 - touch_pad_set_cnt_mode(pad_num, TOUCH_PAD_SLOPE_7, TOUCH_PAD_TIE_OPT_DEFAULT); + touch_ll_set_charge_speed(pad_num, TOUCH_CHARGE_SPEED_7); #else touch_ll_set_internal_capacitor(0); #endif @@ -113,31 +99,6 @@ void tearDown(void) { * Test Touch read on all available channels - compare values if reading is right */ void test_touch_read(void) { - -#if SOC_TOUCH_SENSOR_VERSION <= 2 - //TEST RELEASE STATE - for (int i = 0; i < sizeof(TOUCH_GPIOS); i++) { -#ifdef CONFIG_IDF_TARGET_ESP32 - TEST_ASSERT_GREATER_THAN(RELEASED_VALUE, touchRead(TOUCH_GPIOS[i])); -#else - TEST_ASSERT_LESS_THAN(RELEASED_VALUE, touchRead(TOUCH_GPIOS[i])); -#endif - } - - // TEST PRESS STATE - for (int j = 0; j < TEST_TOUCH_CHANNEL; j++) { - test_press_fake(touch_list[j]); - } - delay(100); - - for (int k = 0; k < sizeof(TOUCH_GPIOS); k++) { -#ifdef CONFIG_IDF_TARGET_ESP32 - TEST_ASSERT_LESS_THAN(PRESSED_VALUE, touchRead(TOUCH_GPIOS[k])); -#else - TEST_ASSERT_GREATER_THAN(PRESSED_VALUE, touchRead(TOUCH_GPIOS[k])); -#endif - } -#else //TOUCH V3 //TEST RELEASE STATE touch_value_t touch_unpressed[sizeof(TOUCH_GPIOS)]; for (int i = 0; i < sizeof(TOUCH_GPIOS); i++) { @@ -155,11 +116,18 @@ void test_touch_read(void) { touch_pressed[k] = touchRead(TOUCH_GPIOS[k]); } - // COMPARE PRESSED > UNPRESSED + // COMPARE PRESSED <-> UNPRESSED for (int l = 0; l < sizeof(TOUCH_GPIOS); l++) { - TEST_ASSERT_GREATER_THAN((touch_unpressed[l] + PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); + //log_i("Touch %d: %d -> %d", TOUCH_GPIOS[l], touch_unpressed[l], touch_pressed[l]); + Serial.printf("Touch %d: %lu -> %lu\n", TOUCH_GPIOS[l], touch_unpressed[l], touch_pressed[l]); } + for (int l = 0; l < sizeof(TOUCH_GPIOS); l++) { +#if CONFIG_IDF_TARGET_ESP32 + TEST_ASSERT_LESS_THAN((touch_unpressed[l] - PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); +#else + TEST_ASSERT_GREATER_THAN((touch_unpressed[l] + PRESSED_VALUE_DIFFERENCE), touch_pressed[l]); #endif + } } void test_touch_interrtupt(void) { @@ -170,7 +138,7 @@ void test_touch_interrtupt(void) { test_press_fake(touch_list[0]); test_press_fake(touch_list[1]); - delay(300); + delay(100); touchDetachInterrupt(TOUCH_GPIOS[0]); touchDetachInterrupt(TOUCH_GPIOS[1]); @@ -190,6 +158,10 @@ void setup() { ; } +#if SOC_TOUCH_SENSOR_VERSION == 3 + touch_ll_enable_internal_capacitor(true); +#endif + UNITY_BEGIN(); RUN_TEST(test_touch_read); RUN_TEST(test_touch_interrtupt); diff --git a/tools/espota.exe b/tools/espota.exe index 8bee0c9036f..6b132d5b950 100644 Binary files a/tools/espota.exe and b/tools/espota.exe differ diff --git a/tools/espota.py b/tools/espota.py index fd95955a2f3..e474aeffe3b 100755 --- a/tools/espota.py +++ b/tools/espota.py @@ -94,7 +94,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, return 1 content_size = os.path.getsize(filename) - file_md5 = hashlib.md5(open(filename, "rb").read()).hexdigest() + with open(filename, "rb") as f: + file_md5 = hashlib.md5(f.read()).hexdigest() logging.info("Upload size: %d", content_size) message = "%d %d %d %s\n" % (command, local_port, content_size, file_md5) @@ -118,7 +119,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, return 1 sock2.settimeout(TIMEOUT) try: - data = sock2.recv(37).decode() + data = sock2.recv(69).decode() # "AUTH " + 64-char SHA256 nonce break except: # noqa: E722 sys.stderr.write(".") @@ -132,18 +133,32 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, if data != "OK": if data.startswith("AUTH"): nonce = data.split()[1] + + # Generate client nonce (cnonce) cnonce_text = "%s%u%s%s" % (filename, content_size, file_md5, remote_addr) - cnonce = hashlib.md5(cnonce_text.encode()).hexdigest() - passmd5 = hashlib.md5(password.encode()).hexdigest() - result_text = "%s:%s:%s" % (passmd5, nonce, cnonce) - result = hashlib.md5(result_text.encode()).hexdigest() + cnonce = hashlib.sha256(cnonce_text.encode()).hexdigest() + + # PBKDF2-HMAC-SHA256 challenge/response protocol + # The ESP32 stores the password as SHA256 hash, so we need to hash the password first + # 1. Hash the password with SHA256 (to match ESP32 storage) + password_hash = hashlib.sha256(password.encode()).hexdigest() + + # 2. Derive key using PBKDF2-HMAC-SHA256 with the password hash + salt = nonce + ":" + cnonce + derived_key = hashlib.pbkdf2_hmac("sha256", password_hash.encode(), salt.encode(), 10000) + derived_key_hex = derived_key.hex() + + # 3. Create challenge response + challenge = derived_key_hex + ":" + nonce + ":" + cnonce + response = hashlib.sha256(challenge.encode()).hexdigest() + sys.stderr.write("Authenticating...") sys.stderr.flush() - message = "%d %s %s\n" % (AUTH, cnonce, result) + message = "%d %s %s\n" % (AUTH, cnonce, response) sock2.sendto(message.encode(), remote_address) sock2.settimeout(10) try: - data = sock2.recv(32).decode() + data = sock2.recv(64).decode() # SHA256 produces 64 character response except: # noqa: E722 sys.stderr.write("FAIL\n") logging.error("No Answer to our Authentication") @@ -163,6 +178,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, sock2.close() logging.info("Waiting for device...") + try: sock.settimeout(10) connection, client_address = sock.accept() @@ -172,6 +188,7 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, logging.error("No response from device") sock.close() return 1 + try: with open(filename, "rb") as f: if PROGRESS: @@ -225,7 +242,8 @@ def serve(remote_addr, local_addr, remote_port, local_port, password, filename, logging.error("Error response from device") connection.close() return 1 - + except Exception as e: # noqa: E722 + logging.error("Error: %s", str(e)) finally: connection.close() diff --git a/tools/get.exe b/tools/get.exe index b56f2b98384..bc9abbad4be 100644 Binary files a/tools/get.exe and b/tools/get.exe differ diff --git a/tools/get.py b/tools/get.py index c791020b7e9..808d995d6b4 100755 --- a/tools/get.py +++ b/tools/get.py @@ -50,6 +50,49 @@ dist_dir = current_dir + "/dist/" +def is_safe_archive_path(path): + # Check for absolute paths (both Unix and Windows style) + if path.startswith("/") or (len(path) > 1 and path[1] == ":" and path[2] in "\\/"): + raise ValueError(f"Absolute path not allowed: {path}") + + # Normalize the path to handle any path separators + normalized_path = os.path.normpath(path) + + # Check for directory traversal attempts using normalized path + if ".." in normalized_path.split(os.sep): + raise ValueError(f"Directory traversal not allowed: {path}") + + # Additional check for paths that would escape the target directory + if normalized_path.startswith(".."): + raise ValueError(f"Path would escape target directory: {path}") + + # Check for any remaining directory traversal patterns in the original path + # This catches cases that might not be normalized properly + path_parts = path.replace("\\", "/").split("/") + if ".." in path_parts: + raise ValueError(f"Directory traversal not allowed: {path}") + + return True + + +def safe_tar_extract(tar_file, destination): + # Validate all paths before extraction + for member in tar_file.getmembers(): + is_safe_archive_path(member.name) + + # If all paths are safe, proceed with extraction + tar_file.extractall(destination, filter="tar") + + +def safe_zip_extract(zip_file, destination): + # Validate all paths before extraction + for name in zip_file.namelist(): + is_safe_archive_path(name) + + # If all paths are safe, proceed with extraction + zip_file.extractall(destination) + + def sha256sum(filename, blocksize=65536): hash = hashlib.sha256() with open(filename, "rb") as f: @@ -212,6 +255,10 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901 print("File corrupted or incomplete!") cfile = None file_is_corrupted = True + except ValueError as e: + print(f"Security validation failed: {e}") + cfile = None + file_is_corrupted = True if file_is_corrupted: corrupted_filename = filename + ".corrupted" @@ -243,15 +290,15 @@ def unpack(filename, destination, force_extract, checksum): # noqa: C901 if filename.endswith("tar.gz"): if not cfile: cfile = tarfile.open(filename, "r:gz") - cfile.extractall(destination, filter="tar") + safe_tar_extract(cfile, destination) elif filename.endswith("tar.xz"): if not cfile: cfile = tarfile.open(filename, "r:xz") - cfile.extractall(destination, filter="tar") + safe_tar_extract(cfile, destination) elif filename.endswith("zip"): if not cfile: cfile = zipfile.ZipFile(filename) - cfile.extractall(destination) + safe_zip_extract(cfile, destination) else: raise NotImplementedError("Unsupported archive type") @@ -348,9 +395,8 @@ def get_tool(tool, force_download, force_extract): urlretrieve(url, local_path, report_progress, context=ctx) elif "Windows" in sys_name: r = requests.get(url) - f = open(local_path, "wb") - f.write(r.content) - f.close() + with open(local_path, "wb") as f: + f.write(r.content) else: is_ci = os.environ.get("GITHUB_WORKSPACE") if is_ci: @@ -374,7 +420,8 @@ def get_tool(tool, force_download, force_extract): def load_tools_list(filename, platform): - tools_info = json.load(open(filename))["packages"][0]["tools"] + with open(filename, "r") as f: + tools_info = json.load(f)["packages"][0]["tools"] tools_to_download = [] for t in tools_info: if platform == "x86_64-mingw32": diff --git a/variants/XIAO_ESP32C5/pins_arduino.h b/variants/XIAO_ESP32C5/pins_arduino.h new file mode 100644 index 00000000000..eaf6ba07b88 --- /dev/null +++ b/variants/XIAO_ESP32C5/pins_arduino.h @@ -0,0 +1,42 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x2886 +#define USB_PID 0x0067 + +static const uint8_t LED_BUILTIN = 27; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN + +static const uint8_t TX = 11; +static const uint8_t RX = 12; + +static const uint8_t SDA = 23; +static const uint8_t SCL = 24; + +static const uint8_t SS = 7; +static const uint8_t MOSI = 10; +static const uint8_t MISO = 9; +static const uint8_t SCK = 8; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; + +static const uint8_t D0 = 1; +static const uint8_t D1 = 0; +static const uint8_t D2 = 25; +static const uint8_t D3 = 7; +static const uint8_t D4 = 23; +static const uint8_t D5 = 24; +static const uint8_t D6 = 11; +static const uint8_t D7 = 12; +static const uint8_t D8 = 8; +static const uint8_t D9 = 9; +static const uint8_t D10 = 10; + +#endif /* Pins_Arduino_h */ diff --git a/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin b/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin index b11e5b236f0..465e294717f 100644 Binary files a/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin and b/variants/XIAO_ESP32S3/bootloader-tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3/tinyuf2.bin b/variants/XIAO_ESP32S3/tinyuf2.bin index 86d981f8019..95d1c4cb53b 100644 Binary files a/variants/XIAO_ESP32S3/tinyuf2.bin and b/variants/XIAO_ESP32S3/tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin index b11e5b236f0..465e294717f 100644 Binary files a/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin and b/variants/XIAO_ESP32S3_Plus/bootloader-tinyuf2.bin differ diff --git a/variants/XIAO_ESP32S3_Plus/tinyuf2.bin b/variants/XIAO_ESP32S3_Plus/tinyuf2.bin index 86d981f8019..95d1c4cb53b 100644 Binary files a/variants/XIAO_ESP32S3_Plus/tinyuf2.bin and b/variants/XIAO_ESP32S3_Plus/tinyuf2.bin differ diff --git a/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin index ef4e3bf6fd1..65cadafb884 100644 Binary files a/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_camera_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_camera_esp32s3/tinyuf2.bin b/variants/adafruit_camera_esp32s3/tinyuf2.bin index 5eb3429b516..b1b4931d4b8 100644 Binary files a/variants/adafruit_camera_esp32s3/tinyuf2.bin and b/variants/adafruit_camera_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin index 0c49080d571..c87c70a9b32 100644 Binary files a/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2/tinyuf2.bin b/variants/adafruit_feather_esp32s2/tinyuf2.bin index 40a8f04f924..f74a61266f5 100644 Binary files a/variants/adafruit_feather_esp32s2/tinyuf2.bin and b/variants/adafruit_feather_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin index e4d6a498eb1..bb4b220b64b 100644 Binary files a/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2_reversetft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin b/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin index ff6eb115dba..249593bc488 100644 Binary files a/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin and b/variants/adafruit_feather_esp32s2_reversetft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin index aa7cb519661..3d1c246e9a4 100644 Binary files a/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s2_tft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin b/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin index b8334f98220..839edd1c351 100644 Binary files a/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin and b/variants/adafruit_feather_esp32s2_tft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin index 8a04549dad5..f7312390e75 100644 Binary files a/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3/tinyuf2.bin b/variants/adafruit_feather_esp32s3/tinyuf2.bin index d76bd75c297..62d0b6b3ae9 100644 Binary files a/variants/adafruit_feather_esp32s3/tinyuf2.bin and b/variants/adafruit_feather_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin index 9cca9d28a05..199cdabc2c9 100644 Binary files a/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_nopsram/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin b/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin index c345a5b8ce5..dda663678ac 100644 Binary files a/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_nopsram/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin index 4e92bba7a71..e822c1f875a 100644 Binary files a/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_reversetft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin b/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin index 70f80cf1dd3..86345d6483b 100644 Binary files a/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_reversetft/tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin b/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin index 2babca98301..2ab3ebc0d18 100644 Binary files a/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin and b/variants/adafruit_feather_esp32s3_tft/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin b/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin index 1f2b1d66157..035ba42a511 100644 Binary files a/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin and b/variants/adafruit_feather_esp32s3_tft/tinyuf2.bin differ diff --git a/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin index 7e153e6b117..e4146b191f8 100644 Binary files a/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_funhouse_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_funhouse_esp32s2/tinyuf2.bin b/variants/adafruit_funhouse_esp32s2/tinyuf2.bin index 4cf847944f7..1e75fb3b404 100644 Binary files a/variants/adafruit_funhouse_esp32s2/tinyuf2.bin and b/variants/adafruit_funhouse_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin index eec06c84fa2..5f5afe53b87 100644 Binary files a/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_magtag29_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_magtag29_esp32s2/tinyuf2.bin b/variants/adafruit_magtag29_esp32s2/tinyuf2.bin index 7a70be3ef32..5b1c92e019a 100644 Binary files a/variants/adafruit_magtag29_esp32s2/tinyuf2.bin and b/variants/adafruit_magtag29_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin index 6f062fea2fe..ed3a4aab55f 100644 Binary files a/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_matrixportal_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin b/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin index 16f7054546b..d671d114d61 100644 Binary files a/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin and b/variants/adafruit_matrixportal_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin index 1a98daf73f7..434a61ceaa9 100644 Binary files a/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_metro_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s2/tinyuf2.bin b/variants/adafruit_metro_esp32s2/tinyuf2.bin index 6763a41df29..4350be6268c 100644 Binary files a/variants/adafruit_metro_esp32s2/tinyuf2.bin and b/variants/adafruit_metro_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin b/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin index 03cc2bfe4cf..0ef7720ad5b 100644 Binary files a/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin and b/variants/adafruit_metro_esp32s3/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_metro_esp32s3/tinyuf2.bin b/variants/adafruit_metro_esp32s3/tinyuf2.bin index d0eaed3f7cf..aea78b0a4d3 100644 Binary files a/variants/adafruit_metro_esp32s3/tinyuf2.bin and b/variants/adafruit_metro_esp32s3/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin index f34e2710bd9..5ce1a3ba476 100644 Binary files a/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s2/tinyuf2.bin b/variants/adafruit_qtpy_esp32s2/tinyuf2.bin index 008c8a1f9c4..3ce657e272a 100644 Binary files a/variants/adafruit_qtpy_esp32s2/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s2/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin index 301ccf6088c..e82118ab400 100644 Binary files a/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_n4r2/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin index 887fd31be65..2526253b577 100644 Binary files a/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_n4r2/tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin index ff0868213e8..d4e57a792e2 100644 Binary files a/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_nopsram/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin b/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin index 5dfee90e6e5..328e67b1eb8 100644 Binary files a/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin and b/variants/adafruit_qtpy_esp32s3_nopsram/tinyuf2.bin differ diff --git a/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin b/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin index 0d437a3e3a3..7b6ba141439 100644 Binary files a/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin and b/variants/adafruit_qualia_s3_rgb666/bootloader-tinyuf2.bin differ diff --git a/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin b/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin index ed8e9df4bcd..9d7025242c9 100644 Binary files a/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin and b/variants/adafruit_qualia_s3_rgb666/tinyuf2.bin differ diff --git a/variants/axiometa_pixie_m1/pins_arduino.h b/variants/axiometa_pixie_m1/pins_arduino.h new file mode 100644 index 00000000000..d318030decc --- /dev/null +++ b/variants/axiometa_pixie_m1/pins_arduino.h @@ -0,0 +1,70 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +// Pixie has a built in RGB LED WS2812B and a regular LED +#define PIN_RGB_LED 21 +#define PIN_LED 11 +// Regular built-in LED (pin 11) - for use with digitalWrite() +#define LED_BUILTIN PIN_LED +#define BUILTIN_LED LED_BUILTIN // backward compatibility +// RGB LED (pin 21) - use with RGB library functions +#define RGB_LED PIN_RGB_LED +// Allow testing for LED_BUILTIN +#ifdef LED_BUILTIN +// Defined and ready to use +#endif + +static const uint8_t TX = 43; +static const uint8_t RX = 44; + +static const uint8_t SDA = 10; +static const uint8_t SCL = 11; + +static const uint8_t SS = 1; +static const uint8_t MOSI = 12; +static const uint8_t MISO = 13; +static const uint8_t SCK = 14; + +static const uint8_t A0 = 1; +static const uint8_t A1 = 2; +static const uint8_t A2 = 3; +static const uint8_t A3 = 4; +static const uint8_t A4 = 5; +static const uint8_t A5 = 6; +static const uint8_t A6 = 7; +static const uint8_t A7 = 8; +static const uint8_t A8 = 9; +static const uint8_t A9 = 10; +static const uint8_t A10 = 11; +static const uint8_t A11 = 12; +static const uint8_t A12 = 13; +static const uint8_t A13 = 14; +static const uint8_t A14 = 15; +static const uint8_t A15 = 16; +static const uint8_t A16 = 17; +static const uint8_t A17 = 18; +static const uint8_t A18 = 19; +static const uint8_t A19 = 20; + +static const uint8_t T1 = 1; +static const uint8_t T2 = 2; +static const uint8_t T3 = 3; +static const uint8_t T4 = 4; +static const uint8_t T5 = 5; +static const uint8_t T6 = 6; +static const uint8_t T7 = 7; +static const uint8_t T8 = 8; +static const uint8_t T9 = 9; +static const uint8_t T10 = 10; +static const uint8_t T11 = 11; +static const uint8_t T12 = 12; +static const uint8_t T13 = 13; +static const uint8_t T14 = 14; + +#endif /* Pins_Arduino_h */ diff --git a/variants/esp32p4_4ds_mipi/pins_arduino.h b/variants/esp32p4_4ds_mipi/pins_arduino.h new file mode 100644 index 00000000000..d4a11090b0c --- /dev/null +++ b/variants/esp32p4_4ds_mipi/pins_arduino.h @@ -0,0 +1,73 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// Use default UART0 pins +static const uint8_t TX = 37; +static const uint8_t RX = 38; + +// Default pins (7 and 8) are used by on-board components already, +// for libraries, this can be set manually +// so let's keep the default for the user +static const uint8_t SDA = 2; // careful, also used as T0 pin +static const uint8_t SCL = 3; // careful, also used as T1 pin + +static const uint8_t SCK = 6; // careful, also used as T2 pin +static const uint8_t MOSI = 14; // careful, also used as T1 pin +static const uint8_t MISO = 15; // careful, also used as T0 pin +static const uint8_t SS = 16; // careful, also used as A9 pin + +static const uint8_t A0 = 21; +static const uint8_t A1 = 20; +static const uint8_t A2 = 19; +static const uint8_t A3 = 18; +static const uint8_t A4 = 17; +static const uint8_t A5 = 52; +static const uint8_t A6 = 51; +static const uint8_t A7 = 50; +static const uint8_t A8 = 49; +static const uint8_t A9 = 16; // careful, also used as SPI SS pin + +static const uint8_t T0 = 15; // careful, also used as SPI MISO pin +static const uint8_t T1 = 14; // careful, also used as SPI MOSI pin +static const uint8_t T2 = 6; // careful, also used as SPI SCK pin +static const uint8_t T3 = 3; // careful, also used as I2C SCL pin +static const uint8_t T4 = 2; // careful, also used as I2C SDA pin + +/* 4D Systems ESP32-P4 board specific definitions */ +// LCD +#define LCD_INTERFACE_MIPI + +#define LCD_BL_IO 22 +#define LCD_BL_ON_LEVEL 1 +#define LCD_BL_OFF_LEVEL !LCD_BL_ON_LEVEL + +#define LCD_RST_IO 23 +#define LCD_RST_ACTIVE_HIGH true + +// I2C for on-board components +#define I2C_SDA 7 +#define I2C_SCL 8 + +// Touch +#define CTP_RST 4 +#define CTP_INT 5 + +// Audio +#define AMP_CTRL 53 +#define I2S_DSDIN 9 +#define I2S_LRCK 10 +#define I2S_ASDOUT 11 +#define I2S_SCLK 12 +#define I2S_MCLK 13 + +// SDMMC +#define BOARD_HAS_SDMMC +#define BOARD_SDMMC_SLOT 0 +#define BOARD_SDMMC_POWER_CHANNEL 4 +#define BOARD_SDMMC_POWER_PIN 45 +#define BOARD_SDMMC_POWER_ON_LEVEL LOW + +#endif /* Pins_Arduino_h */ diff --git a/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin b/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin new file mode 100644 index 00000000000..b83265965f4 Binary files /dev/null and b/variants/fobe_quill_esp32s3_mesh/bootloader-tinyuf2.bin differ diff --git a/variants/fobe_quill_esp32s3_mesh/pins_arduino.h b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h new file mode 100644 index 00000000000..491d75333a7 --- /dev/null +++ b/variants/fobe_quill_esp32s3_mesh/pins_arduino.h @@ -0,0 +1,107 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303A +#define USB_PID 0x82F4 +#define USB_MANUFACTURER "FoBE Studio" +#define USB_PRODUCT "FoBE Quill ESP32S3 Mesh" +#define USB_SERIAL "" // Empty string for MAC address + +// User LED +#define LED_BUILTIN 11 +#define BUILTIN_LED LED_BUILTIN // backward compatibility + +/* + * Battery + */ +#define PIN_VBAT (10) + +/* + * Buttons + */ +#define PIN_BUTTON1 (0) + +/* + * Serial interfaces + */ +static const uint8_t TX = 9; +static const uint8_t RX = 8; + +/* + * Wire Interfaces + */ +static const uint8_t SDA = 14; +static const uint8_t SCL = 13; + +/* + * SPI interfaces + */ +static const uint8_t SS = 45; +static const uint8_t MOSI = 39; +static const uint8_t SCK = 40; +static const uint8_t MISO = 41; + +/* + * Screen + */ +#define PIN_OLED_SDA (14) +#define PIN_OLED_SCL (13) +#define PIN_OLED_EN (12) + +/* + * LoRa + */ +#define PIN_SX126X_NSS (45) +#define PIN_SX126X_DIO1 (42) +#define PIN_SX126X_BUSY (43) +#define PIN_SX126X_RESET (44) +#define PIN_SX126X_TXEN (-1) +#define PIN_SX126X_RXEN (46) +#define SX126X_DIO2_AS_RF_SWITCH +#define SX126X_DIO3_TCXO_VOLTAGE 1.8 + +/* + * MFP + */ +#define PIN_MFP1 (38) +#define PIN_MFP2 (37) +#define PIN_MFP3 (36) +#define PIN_MFP4 (35) + +/* + * Power + */ +#define PIN_PERI_EN (1) + +/* + * PINs + */ +static const uint8_t A0 = 2; +static const uint8_t A1 = 3; +static const uint8_t A2 = 4; +static const uint8_t A3 = 5; +static const uint8_t A4 = 6; +static const uint8_t A5 = 7; +static const uint8_t D0 = 8; +static const uint8_t D1 = 9; +static const uint8_t D2 = 11; +static const uint8_t D3 = 38; +static const uint8_t D4 = 37; +static const uint8_t D5 = 36; +static const uint8_t D6 = 35; +static const uint8_t D7 = 34; +static const uint8_t D8 = 33; +static const uint8_t D9 = 47; +static const uint8_t D10 = 48; +static const uint8_t D11 = 21; +static const uint8_t D12 = 18; +static const uint8_t D13 = 17; +static const uint8_t MTCK = 39; +static const uint8_t MTDO = 40; +static const uint8_t MTDI = 41; +static const uint8_t MTMS = 42; + +#endif /* Pins_Arduino_h */ diff --git a/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin b/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin new file mode 100644 index 00000000000..b854b19518f Binary files /dev/null and b/variants/fobe_quill_esp32s3_mesh/tinyuf2.bin differ diff --git a/variants/fobe_quill_esp32s3_mesh/variant.cpp b/variants/fobe_quill_esp32s3_mesh/variant.cpp new file mode 100644 index 00000000000..32ef46f053b --- /dev/null +++ b/variants/fobe_quill_esp32s3_mesh/variant.cpp @@ -0,0 +1,15 @@ +#include "esp32-hal-gpio.h" +#include "pins_arduino.h" + +extern "C" { + +void initVariant(void) { + // Turn on the peripheral power + pinMode(PIN_PERI_EN, OUTPUT); + digitalWrite(PIN_PERI_EN, HIGH); + + // Turn on the OLED power + pinMode(PIN_OLED_EN, OUTPUT); + digitalWrite(PIN_OLED_EN, LOW); +} +} diff --git a/variants/lilygo_t3_s3_sx127x/pins_arduino.h b/variants/lilygo_t3_s3_sx127x/pins_arduino.h index 1b3e0a99239..8caf05582c9 100644 --- a/variants/lilygo_t3_s3_sx127x/pins_arduino.h +++ b/variants/lilygo_t3_s3_sx127x/pins_arduino.h @@ -35,7 +35,6 @@ static const uint8_t SCK = 14; #define LORA_CS 7 // SX1276/SX1278 CS #define LORA_RST 8 // SX1276/SX1278 RST -#define LORA_BUSY 33 #define LORA_DIO0 9 //IRQ #define LORA_DIO1 33 #define LORA_DIO2 34 diff --git a/variants/m5stack_atoms3/pins_arduino.h b/variants/m5stack_atoms3/pins_arduino.h index 9d2389cc98c..739cddf3acd 100644 --- a/variants/m5stack_atoms3/pins_arduino.h +++ b/variants/m5stack_atoms3/pins_arduino.h @@ -7,10 +7,10 @@ #define USB_VID 0x303a #define USB_PID 0x1001 -// Some boards have too low voltage on this pin (board design bug) -// Use different pin with 3V and connect with 48 -// and change this setup for the chosen pin (for example 38) -static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + 48; +// The board RGB led is connected to GPIO #35 +#define PIN_RGB_LED 35 +// BUILTIN_LED can be used in new Arduino API digitalWrite() like in Blink.ino +static const uint8_t LED_BUILTIN = SOC_GPIO_PIN_COUNT + PIN_RGB_LED; #define BUILTIN_LED LED_BUILTIN // backward compatibility #define LED_BUILTIN LED_BUILTIN #define RGB_BUILTIN LED_BUILTIN diff --git a/variants/m5stack_tab5/pins_arduino.h b/variants/m5stack_tab5/pins_arduino.h new file mode 100644 index 00000000000..df9c1fb0d65 --- /dev/null +++ b/variants/m5stack_tab5/pins_arduino.h @@ -0,0 +1,62 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +// BOOT_MODE 35 +// BOOT_MODE2 36 pullup + +static const uint8_t TX = 37; +static const uint8_t RX = 38; + +static const uint8_t SDA = 53; +static const uint8_t SCL = 54; + +// Use GPIOs 36 or lower on the P4 DevKit to avoid LDO power issues with high numbered GPIOs. +static const uint8_t SS = 26; +static const uint8_t MOSI = 32; +static const uint8_t MISO = 33; +static const uint8_t SCK = 36; + +static const uint8_t A0 = 16; +static const uint8_t A1 = 17; +static const uint8_t A2 = 18; +static const uint8_t A3 = 19; +static const uint8_t A4 = 20; +static const uint8_t A5 = 21; +static const uint8_t A6 = 22; +static const uint8_t A7 = 23; +static const uint8_t A8 = 49; +static const uint8_t A9 = 50; +static const uint8_t A10 = 51; +static const uint8_t A11 = 52; +static const uint8_t A12 = 53; +static const uint8_t A13 = 54; + +static const uint8_t T0 = 2; +static const uint8_t T1 = 3; +static const uint8_t T2 = 4; +static const uint8_t T3 = 5; +static const uint8_t T4 = 6; +static const uint8_t T5 = 7; +static const uint8_t T6 = 8; +static const uint8_t T7 = 9; +static const uint8_t T8 = 10; +static const uint8_t T9 = 11; +static const uint8_t T10 = 12; +static const uint8_t T11 = 13; +static const uint8_t T12 = 14; +static const uint8_t T13 = 15; + +//WIFI - ESP32C6 +#define BOARD_HAS_SDIO_ESP_HOSTED +#define BOARD_SDIO_ESP_HOSTED_CLK 12 +#define BOARD_SDIO_ESP_HOSTED_CMD 13 +#define BOARD_SDIO_ESP_HOSTED_D0 11 +#define BOARD_SDIO_ESP_HOSTED_D1 10 +#define BOARD_SDIO_ESP_HOSTED_D2 9 +#define BOARD_SDIO_ESP_HOSTED_D3 8 +#define BOARD_SDIO_ESP_HOSTED_RESET 15 + +#endif /* Pins_Arduino_h */ diff --git a/variants/mant1s/pins_arduino.h b/variants/mant1s/pins_arduino.h new file mode 100644 index 00000000000..b0b7e54ffaa --- /dev/null +++ b/variants/mant1s/pins_arduino.h @@ -0,0 +1,38 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +static const uint8_t TX = 1; +static const uint8_t RX = 3; + +static const uint8_t SCL = 32; +static const uint8_t SDA = 33; + +static const uint8_t SS = 15; +static const uint8_t MOSI = 13; +static const uint8_t MISO = 12; +static const uint8_t SCK = 14; + +static const uint8_t A0 = 36; +static const uint8_t A1 = 37; +static const uint8_t A2 = 38; +static const uint8_t A3 = 39; +static const uint8_t A6 = 34; +static const uint8_t A7 = 35; + +static const uint8_t T0 = 4; +static const uint8_t T2 = 2; +static const uint8_t T3 = 15; +static const uint8_t T4 = 13; +static const uint8_t T5 = 12; +static const uint8_t T6 = 14; + +#define ETH_PHY_ADDR 0 +#define ETH_PHY_POWER -1 +#define ETH_PHY_MDC 8 +#define ETH_PHY_MDIO 7 +#define ETH_PHY_TYPE ETH_PHY_LAN867X +#define ETH_CLK_MODE ETH_CLOCK_GPIO0_IN + +#endif /* Pins_Arduino_h */ diff --git a/variants/sensebox_eye/APOTA.bin b/variants/sensebox_eye/APOTA.bin new file mode 100644 index 00000000000..18547ceb255 Binary files /dev/null and b/variants/sensebox_eye/APOTA.bin differ diff --git a/variants/sensebox_eye/APOTA.ino b/variants/sensebox_eye/APOTA.ino new file mode 100644 index 00000000000..58c1116cc70 --- /dev/null +++ b/variants/sensebox_eye/APOTA.ino @@ -0,0 +1,301 @@ +// APOTA is an Arduino fallback sketch that is written to OTA1_Partition. +// APOTA opens an access point which waits to receive a .bin file on /sketch. +// After successful upload, the file is written to OTA0_Partition, and the microcontroller reboots to the newly uploaded sketch. + +#define DISPLAY_ENABLED + +#include +#include +#include +#include +#include +#include +#ifdef DISPLAY_ENABLED +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 +#include +#include +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); +#include +Adafruit_NeoPixel rgb_led = Adafruit_NeoPixel(1, PIN_LED, NEO_GRB + NEO_KHZ800); + +#endif +#include "esp_partition.h" +#include "esp_ota_ops.h" +#include "esp_system.h" + +String ssid; +uint8_t mac[6]; + +// Create an instance of the server +WebServer server(80); +bool displayEnabled; + +const int BUTTON_PIN = 0; // GPIO for the button +volatile unsigned long lastPressTime = 0; // Time of last button press +volatile bool doublePressDetected = false; // Flag for double press +const unsigned long doublePressInterval = 500; // Max. time (in ms) between two presses for double press +volatile int pressCount = 0; // Counts the button presses + +const unsigned char epd_bitmap_wifi[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x07, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x1f, 0xe0, 0xff, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x0f, 0x80, 0x00, 0x00, 0x7c, 0x00, 0x03, 0xe0, 0x00, 0x00, 0xf0, 0x00, 0x01, 0xf0, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x78, 0x00, + 0x03, 0xc0, 0x00, 0x00, 0x38, 0x00, 0x07, 0x80, 0x00, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x0e, 0x00, 0x7f, 0xe0, 0x0e, 0x00, + 0x0c, 0x01, 0xff, 0xf0, 0x06, 0x00, 0x00, 0x07, 0xff, 0xfc, 0x02, 0x00, 0x00, 0x0f, 0x80, 0x3e, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x07, 0x80, 0x00, 0x00, 0x38, 0x00, 0x03, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x01, 0xc0, 0x00, 0x00, 0x70, 0x00, 0x00, 0xc0, 0x00, + 0x00, 0x20, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xe0, 0x00, 0x00, + 0x00, 0x01, 0xe0, 0xf0, 0x00, 0x00, 0x00, 0x01, 0xc0, 0x78, 0x00, 0x00, 0x00, 0x03, 0x80, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// 'checkmark', 44x44px +const unsigned char epd_bitmap_checkmark[] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xf0, 0x00, 0x00, + 0x00, 0x0f, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x0f, 0x83, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xc7, 0x80, 0x00, 0x00, 0x00, 0x03, 0xef, 0x00, 0x00, 0x00, + 0x00, 0x01, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +void IRAM_ATTR handleButtonPress() { + unsigned long currentTime = millis(); // Get current time + + // Debounce: If the current press is too close to the last one, ignore it + if (currentTime - lastPressTime > 50) { + pressCount++; // Count the button press + + // Check if this is the second press within the double-press interval + if (pressCount == 2 && (currentTime - lastPressTime <= doublePressInterval)) { + doublePressDetected = true; // Double press detected + pressCount = 0; // Reset counter + } + + lastPressTime = currentTime; // Update the time of the last press + } +} + +// Function to switch the boot partition to OTA0 +void setBootPartitionToOTA0() { + const esp_partition_t *ota0_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_0, NULL); + + if (ota0_partition) { + // Set OTA0 as new boot partition + esp_ota_set_boot_partition(ota0_partition); + Serial.println("Boot partition changed to OTA0. Restarting..."); + + // Restart to boot from the new partition + esp_restart(); + } else { + Serial.println("OTA0 partition not found!"); + } +} + +void setupDisplay() { + Wire.begin(PIN_QWIIC_SDA, PIN_QWIIC_SCL); + displayEnabled = Wire.requestFrom(0x3D, 1); // Check if the display is connected + if (displayEnabled) { + display.begin(SSD1306_SWITCHCAPVCC, 0x3D); + display.display(); + delay(100); + display.clearDisplay(); + } +} + +void displayStatusBar(int progress) { + display.clearDisplay(); + display.setCursor(16, 8); + display.println("Sketch is being"); + display.setCursor(32, 22); + display.println("uploaded!"); + + display.fillRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, BLACK); // Clear status bar area + display.drawRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, WHITE); // Draw border + int filledWidth = (progress * SCREEN_WIDTH - 4) / 100; // Calculate progress width + display.fillRect(1, SCREEN_HEIGHT - 23, filledWidth - 4, 6, WHITE); // Fill progress bar + + display.setCursor((SCREEN_WIDTH / 2) - 12, SCREEN_HEIGHT - 10); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.print(progress); + display.println(" %"); + display.display(); +} + +void displayWelcomeScreen() { + display.clearDisplay(); + + // Draw WiFi symbol + display.drawBitmap(0, 12, epd_bitmap_wifi, 44, 44, WHITE); + + // Display SSID text + display.setCursor(52, 13); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println("Connect"); + display.setCursor(60, 27); + display.println("with:"); + + // Display SSID + display.setCursor(40, 43); + display.setTextSize(1); // Larger text for SSID + display.print(ssid); + + display.display(); +} + +void displaySuccessScreen() { + display.clearDisplay(); + + // Draw WiFi symbol + display.drawBitmap(0, 12, epd_bitmap_checkmark, 44, 44, WHITE); + + // Display SSID text + display.setCursor(48, 22); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println("Successfully"); + display.setCursor(48, 36); + display.println("uploaded!"); + + display.display(); +} + +void wipeDisplay() { + display.clearDisplay(); + display.println(""); + display.display(); +} + +void setupWiFi() { + WiFi.macAddress(mac); + char macLastFour[5]; + snprintf(macLastFour, sizeof(macLastFour), "%02X%02X", mac[4], mac[5]); + ssid = "senseBox:" + String(macLastFour); + + // Define the IP address, gateway, and subnet mask + IPAddress local_IP(192, 168, 1, 1); // The new IP address + IPAddress gateway(192, 168, 1, 1); // Gateway address (can be the same as the AP's IP) + IPAddress subnet(255, 255, 255, 0); // Subnet mask + + // Set the IP address, gateway, and subnet mask of the access point + WiFi.softAPConfig(local_IP, gateway, subnet); + + // Start the access point + WiFi.softAP(ssid.c_str()); +} + +void setupOTA() { + // Handle updating process + server.on( + "/sketch", HTTP_POST, + []() { + server.sendHeader("Connection", "close"); + server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK"); + ESP.restart(); + }, + []() { + HTTPUpload &upload = server.upload(); + + if (upload.status == UPLOAD_FILE_START) { + Serial.setDebugOutput(true); + size_t fsize = UPDATE_SIZE_UNKNOWN; + if (server.clientContentLength() > 0) { + fsize = server.clientContentLength(); + } + Serial.printf("Receiving Update: %s, Size: %d\n", upload.filename.c_str(), fsize); + + Serial.printf("Update: %s\n", upload.filename.c_str()); + if (!Update.begin(fsize)) { //start with max available size + Update.printError(Serial); + } + } else if (upload.status == UPLOAD_FILE_WRITE) { + /* flashing firmware to ESP*/ + if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) { + Update.printError(Serial); + } else { + int progress = (Update.progress() * 100) / Update.size(); + if (displayEnabled) { + displayStatusBar(progress); // Update progress on display + } + rgb_led.setPixelColor(0, rgb_led.Color(255, 255, 51)); + rgb_led.show(); + } + } else if (upload.status == UPLOAD_FILE_END) { + if (Update.end(true)) { //true to set the size to the current progress + if (displayEnabled) { + displaySuccessScreen(); + delay(3000); + wipeDisplay(); + } + rgb_led.setPixelColor(0, rgb_led.Color(51, 51, 255)); + rgb_led.show(); + } else { + Update.printError(Serial); + } + Serial.setDebugOutput(false); + } + yield(); + } + ); +} + +void setup() { + // Start Serial communication + Serial.begin(115200); + rgb_led.begin(); + rgb_led.setBrightness(15); + rgb_led.setPixelColor(0, rgb_led.Color(51, 51, 255)); + rgb_led.show(); + + // Configure button pin as input + pinMode(BUTTON_PIN, INPUT_PULLUP); + + // Interrupt for the button + attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), handleButtonPress, FALLING); + +#ifdef DISPLAY_ENABLED + setupDisplay(); +#endif + setupWiFi(); + // Set the ESP32 as an access point + setupOTA(); + server.begin(); +} + +void loop() { + // Handle client requests + server.handleClient(); + +#ifdef DISPLAY_ENABLED + if (displayEnabled) { + displayWelcomeScreen(); + } +#endif + + if (doublePressDetected) { + Serial.println("Double press detected!"); + setBootPartitionToOTA0(); +#ifdef DISPLAY_ENABLED + if (displayEnabled) { + display.setCursor(0, 0); + display.setTextSize(1); + display.setTextColor(WHITE, BLACK); + display.println(""); + display.display(); + delay(50); + } +#endif + // Restart to boot from the new partition + esp_restart(); + } +} diff --git a/variants/sensebox_eye/bootloader-tinyuf2.bin b/variants/sensebox_eye/bootloader-tinyuf2.bin new file mode 100644 index 00000000000..7a83be05436 Binary files /dev/null and b/variants/sensebox_eye/bootloader-tinyuf2.bin differ diff --git a/variants/sensebox_eye/partitions-16MB-tinyuf2.csv b/variants/sensebox_eye/partitions-16MB-tinyuf2.csv new file mode 100644 index 00000000000..55dc6213161 --- /dev/null +++ b/variants/sensebox_eye/partitions-16MB-tinyuf2.csv @@ -0,0 +1,10 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +# bootloader.bin,, 0x1000, 32K +# partition table, 0x8000, 4K +nvs, data, nvs, 0x9000, 20K, +otadata, data, ota, 0xe000, 8K, +ota_0, 0, ota_0, 0x10000, 2048K, +ota_1, 0, ota_1, 0x210000, 2048K, +uf2, app, factory,0x410000, 256K, +ffat, data, fat, 0x450000, 11968K, diff --git a/variants/sensebox_eye/pins_arduino.h b/variants/sensebox_eye/pins_arduino.h new file mode 100644 index 00000000000..70c2bd880c9 --- /dev/null +++ b/variants/sensebox_eye/pins_arduino.h @@ -0,0 +1,90 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include + +#define USB_VID 0x303A +#define USB_PID 0x82D1 +#define USB_MANUFACTURER "senseBox" +#define USB_PRODUCT "Eye ESP32S3" +#define USB_SERIAL "" // Empty string for MAC address + +// Default USB FirmwareMSC Settings +#define USB_FW_MSC_VENDOR_ID "senseBox" // max 8 chars +#define USB_FW_MSC_PRODUCT_ID "Eye ESP32S3" // max 16 chars +#define USB_FW_MSC_PRODUCT_REVISION "1.00" // max 4 chars +#define USB_FW_MSC_VOLUME_NAME "senseBox" // max 11 chars +#define USB_FW_MSC_SERIAL_NUMBER 0x00000000 + +#define PIN_RGB_LED 45 // RGB LED +#define RGBLED_PIN 45 // RGB LED +#define PIN_LED 45 +#define RGBLED_NUM 1 // number of RGB LEDs + +// Default I2C QWIIC-Ports +static const uint8_t SDA = 2; +static const uint8_t SCL = 1; +#define PIN_QWIIC_SDA 2 +#define PIN_QWIIC_SCL 1 + +// IO Pins +#define PIN_IO14 14 +static const uint8_t A14 = PIN_IO14; // Analog +static const uint8_t D14 = PIN_IO14; // Digital +static const uint8_t T14 = PIN_IO14; // Touch +#define PIN_IO48 48 +static const uint8_t A48 = PIN_IO48; // Analog +static const uint8_t D48 = PIN_IO48; // Digital +static const uint8_t T48 = PIN_IO48; // Touch + +// Button +#define PIN_BUTTON 47 + +// UART Port +static const uint8_t TX = 43; +static const uint8_t RX = 44; +#define PIN_UART_TXD 43 +#define PIN_UART_RXD 44 +#define PIN_UART_ENABLE 26 + +// SD-Card +#define MISO 40 +#define MOSI 38 +#define SCK 39 +#define SS 41 +#define SD_ENABLE 3 + +#define PIN_SD_MISO 40 +#define PIN_SD_MOSI 38 +#define PIN_SD_SCLK 39 +#define PIN_SD_CS 41 +#define PIN_SD_ENABLE 3 + +// USB +#define PIN_USB_DM 19 +#define PIN_USB_DP 20 + +// Camera +#define PWDN_GPIO_NUM 46 +#define RESET_GPIO_NUM -1 +#define XCLK_GPIO_NUM 15 +#define SIOD_GPIO_NUM 4 +#define SIOC_GPIO_NUM 5 + +#define Y9_GPIO_NUM 16 +#define Y8_GPIO_NUM 17 +#define Y7_GPIO_NUM 18 +#define Y6_GPIO_NUM 12 +#define Y5_GPIO_NUM 10 +#define Y4_GPIO_NUM 8 +#define Y3_GPIO_NUM 9 +#define Y2_GPIO_NUM 11 +#define VSYNC_GPIO_NUM 6 +#define HREF_GPIO_NUM 7 +#define PCLK_GPIO_NUM 13 + +// LoRa +#define LORA_TX 43 +#define LORA_RX 44 + +#endif /* Pins_Arduino_h */ diff --git a/variants/sensebox_eye/tinyuf2.bin b/variants/sensebox_eye/tinyuf2.bin new file mode 100644 index 00000000000..67c00aa39f0 Binary files /dev/null and b/variants/sensebox_eye/tinyuf2.bin differ diff --git a/variants/sensebox_eye/variant.cpp b/variants/sensebox_eye/variant.cpp new file mode 100644 index 00000000000..cd86cdec84c --- /dev/null +++ b/variants/sensebox_eye/variant.cpp @@ -0,0 +1,39 @@ +#include "esp32-hal-gpio.h" +#include "pins_arduino.h" +#include "esp_log.h" +#include "esp_partition.h" +#include "esp_system.h" +#include "esp_ota_ops.h" + +extern "C" { + +void blinkLED(uint8_t r, uint8_t g, uint8_t b) { + rgbLedWrite(PIN_LED, r, g, b); + delay(20); + rgbLedWrite(PIN_LED, 0x00, 0x00, 0x00); // off +} + +void initVariant(void) { + // define button pin + pinMode(47, INPUT_PULLUP); + + // Check if button is pressed + if (digitalRead(47) == LOW) { + // When the button is pressed and then released, boot into the OTA1 partition + const esp_partition_t *ota1_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_1, NULL); + + if (ota1_partition) { + esp_err_t err = esp_ota_set_boot_partition(ota1_partition); + if (err == ESP_OK) { + blinkLED(0x00, 0x00, 0x10); // blue + esp_restart(); // restart, to boot OTA1 partition + } else { + blinkLED(0x10, 0x00, 0x00); // red + ESP_LOGE("OTA", "Error setting OTA1 partition: %s", esp_err_to_name(err)); + } + } + } else { + blinkLED(0x00, 0x10, 0x00); // green + } +} +} diff --git a/variants/sensebox_mcu_esp32s2/APOTA.bin b/variants/sensebox_mcu_esp32s2/APOTA.bin index 0ea39335dce..ebe676c1733 100644 Binary files a/variants/sensebox_mcu_esp32s2/APOTA.bin and b/variants/sensebox_mcu_esp32s2/APOTA.bin differ diff --git a/variants/sensebox_mcu_esp32s2/APOTA.ino b/variants/sensebox_mcu_esp32s2/APOTA.ino index 5f683752abc..ef0fa7d9063 100644 --- a/variants/sensebox_mcu_esp32s2/APOTA.ino +++ b/variants/sensebox_mcu_esp32s2/APOTA.ino @@ -1,3 +1,7 @@ +// APOTA is an Arduino fallback sketch that is written to OTA1_Partition. +// APOTA opens an access point which waits to receive a .bin file on /sketch. +// After successful upload, the file is written to OTA0_Partition, and the microcontroller reboots to the newly uploaded sketch. + #define DISPLAY_ENABLED #include @@ -107,10 +111,10 @@ void setupDisplay() { void displayStatusBar(int progress) { display.clearDisplay(); - display.setCursor(24, 8); - display.println("Sketch wird"); - display.setCursor(22, 22); - display.println("hochgeladen!"); + display.setCursor(16, 8); + display.println("Sketch is being"); + display.setCursor(32, 22); + display.println("uploaded!"); display.fillRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, BLACK); // Clear status bar area display.drawRect(0, SCREEN_HEIGHT - 24, SCREEN_WIDTH - 4, 8, WHITE); // Draw border @@ -132,12 +136,12 @@ void displayWelcomeScreen() { display.drawBitmap(0, 12, epd_bitmap_wifi, 44, 44, WHITE); // Display SSID text - display.setCursor(40, 13); + display.setCursor(52, 13); display.setTextSize(1); display.setTextColor(WHITE, BLACK); - display.println("Verbinde dich"); // "Connect" + display.println("Connect"); display.setCursor(60, 27); - display.println("mit:"); // "with" + display.println("with:"); // Display SSID display.setCursor(40, 43); @@ -157,9 +161,9 @@ void displaySuccessScreen() { display.setCursor(48, 22); display.setTextSize(1); display.setTextColor(WHITE, BLACK); - display.println("Erfolgreich"); // "Successfully" + display.println("Successfully"); display.setCursor(48, 36); - display.println("hochgeladen!"); // "uploaded!" + display.println("uploaded!"); display.display(); } @@ -267,7 +271,7 @@ void loop() { #endif if (doublePressDetected) { - Serial.println("Doppeldruck erkannt!"); // "Double press detected!" + Serial.println("Double press detected!"); setBootPartitionToOTA0(); #ifdef DISPLAY_ENABLED display.setCursor(0, 0); diff --git a/variants/twinaiot/pins_arduino.h b/variants/twinaiot/pins_arduino.h new file mode 100644 index 00000000000..a08f3b7d455 --- /dev/null +++ b/variants/twinaiot/pins_arduino.h @@ -0,0 +1,54 @@ +#ifndef Pins_Arduino_h +#define Pins_Arduino_h + +#include +#include "soc/soc_caps.h" + +#define USB_VID 0x303a +#define USB_PID 0x1001 + +#define RGB_PIN 15 + +static const uint8_t LED_BUILTIN = 35; +#define BUILTIN_LED LED_BUILTIN // backward compatibility +#define LED_BUILTIN LED_BUILTIN // allow testing #ifdef LED_BUILTIN +// RGB_BUILTIN and RGB_BRIGHTNESS can be used in new Arduino API rgbLedWrite() +#define RGB_BUILTIN RGB_PIN + +static const uint8_t TX = 39; +static const uint8_t RX = 40; + +static const uint8_t SDA = 39; +static const uint8_t SCL = 40; + +static const uint8_t SS = 1; +static const uint8_t MOSI = 2; +static const uint8_t MISO = 3; +static const uint8_t SCK = 4; + +static const uint8_t D6_OUT_PIN = 35; +static const uint8_t D9_OUT_PIN = 36; +static const uint8_t D10_OUT_PIN = 10; + +static const uint8_t TOUCH_PIN = 13; + +static const uint8_t TRIG_PIN = 5; // GPIO connected to HC-SR04 TRIG +static const uint8_t ECHO_PIN = 6; // GPIO connected to HC-SR04 ECHO + +static const uint8_t latchPin = 34; +static const uint8_t clockPin = 47; +static const uint8_t dataPin = 48; + +static const uint8_t D_IN_4 = 8; +static const uint8_t D_IN_8 = 11; +static const uint8_t D_IN_12 = 9; + +static const uint8_t AN_IN_4 = 17; +static const uint8_t AN_IN_8 = 16; +static const uint8_t AN_IN_12 = 7; + +static const uint8_t S1pin = 37; +static const uint8_t S2pin = 38; +static const uint8_t S3pin = 14; + +#endif /* Pins_Arduino_h */ diff --git a/variants/watchy/pins_arduino.h b/variants/watchy/pins_arduino.h index c126b27aa06..b77bc3966c1 100644 --- a/variants/watchy/pins_arduino.h +++ b/variants/watchy/pins_arduino.h @@ -29,24 +29,22 @@ static const uint8_t RTC_INT_PIN = 27; #if defined(ARDUINO_WATCHY_V10) static const uint8_t UP_BTN_PIN = 32; static const uint8_t BATT_ADC_PIN = 33; -#define UP_BTN_MASK GPIO_SEL_32 -#define RTC_TYPE 1 //DS3231 +#define RTC_TYPE 1 //DS3231 #elif defined(ARDUINO_WATCHY_V15) static const uint8_t UP_BTN_PIN = 32; static const uint8_t BATT_ADC_PIN = 35; -#define UP_BTN_MASK GPIO_SEL_32 -#define RTC_TYPE 2 //PCF8563 +#define RTC_TYPE 2 //PCF8563 #elif defined(ARDUINO_WATCHY_V20) static const uint8_t UP_BTN_PIN = 35; static const uint8_t BATT_ADC_PIN = 34; -#define UP_BTN_MASK GPIO_SEL_35 -#define RTC_TYPE 2 //PCF8563 +#define RTC_TYPE 2 //PCF8563 #endif -#define MENU_BTN_MASK GPIO_SEL_26 -#define BACK_BTN_MASK GPIO_SEL_25 -#define DOWN_BTN_MASK GPIO_SEL_4 -#define ACC_INT_MASK GPIO_SEL_14 -#define BTN_PIN_MASK MENU_BTN_MASK | BACK_BTN_MASK | UP_BTN_MASK | DOWN_BTN_MASK +#define UP_BTN_MASK (BIT64(UP_BTN_PIN)) +#define MENU_BTN_MASK (BIT64(MENU_BTN_PIN)) +#define BACK_BTN_MASK (BIT64(BACK_BTN_PIN)) +#define DOWN_BTN_MASK (BIT64(DOWN_BTN_PIN)) +#define ACC_INT_MASK (BIT64(ACC_INT_1_PIN)) +#define BTN_PIN_MASK (MENU_BTN_MASK | BACK_BTN_MASK | UP_BTN_MASK | DOWN_BTN_MASK) #endif /* Pins_Arduino_h */