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/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 <sarif_file> <renamed_files_file>")
+        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
index ff0af7da6e8..69cd9a7f7de 100644
--- a/.github/scripts/set_push_chunks.sh
+++ b/.github/scripts/set_push_chunks.sh
@@ -78,7 +78,6 @@ 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/workflows/build_component.yml b/.github/workflows/build_component.yml
new file mode 100644
index 00000000000..5553b4b2024
--- /dev/null
+++ b/.github/workflows/build_component.yml
@@ -0,0 +1,133 @@
+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: true
+  push:
+    branches:
+      - master
+      - release/*
+  pull_request:
+    paths:
+      - "cores/**"
+      - "libraries/**/*.cpp"
+      - "libraries/**/*.c"
+      - "libraries/**/*.h"
+      - "libraries/**/*.ino"
+      - "libraries/**/ci.json"
+      - "idf_component_examples/**"
+      - "idf_component.yml"
+      - "Kconfig.projbuild"
+      - "CMakeLists.txt"
+      - ".github/workflows/build_component.yml"
+      - ".github/scripts/check-cmakelists.sh"
+      - ".github/scripts/on-push-idf.sh"
+      - ".github/scripts/sketch_utils.sh"
+      - "variants/esp32/**"
+      - "variants/esp32c2/**"
+      - "variants/esp32c3/**"
+      - "variants/esp32c6/**"
+      - "variants/esp32h2/**"
+      - "variants/esp32p4/**"
+      - "variants/esp32s2/**"
+      - "variants/esp32s3/**"
+      - "!*.md"
+      - "!*.txt"
+      - "!*.properties"
+
+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:
+      idf_ver: ${{ steps.set-matrix.outputs.idf_ver }}
+      idf_target: ${{ steps.set-matrix.outputs.idf_target }}
+    steps:
+      - name: Get IDF Version and Targets
+        id: set-matrix
+        run: |
+          # Default values
+          idf_ver="release-v5.3,release-v5.4,release-v5.5"
+          idf_targets="esp32,esp32s2,esp32s3,esp32c2,esp32c3,esp32c6,esp32h2,esp32p4"
+
+          # Override with inputs if provided
+          if [[ -n "${{ inputs.idf_ver }}" ]]; then
+            idf_ver="${{ inputs.idf_ver }}"
+          fi
+          if [[ -n "${{ inputs.idf_targets }}" ]]; then
+            idf_targets="${{ inputs.idf_targets }}"
+          fi
+
+          # Convert comma-separated strings to JSON arrays using a more robust method
+          idf_ver_json=$(printf '%s\n' "$idf_ver" | tr ',' '\n' | jq -R . | jq -s . | jq -c .)
+          idf_targets_json=$(printf '%s\n' "$idf_targets" | tr ',' '\n' | jq -R . | jq -s . | jq -c .)
+
+          # Debug: Print the JSON for verification
+          echo "Debug - idf_ver_json: $idf_ver_json"
+          echo "Debug - idf_targets_json: $idf_targets_json"
+
+          # Set outputs - ensure no extra whitespace
+          printf "idf_ver=%s\n" "$idf_ver_json" >> $GITHUB_OUTPUT
+          printf "idf_target=%s\n" "$idf_targets_json" >> $GITHUB_OUTPUT
+
+  build-esp-idf-component:
+    name: Build IDF ${{ matrix.idf_ver }} for ${{ matrix.idf_target }}
+    runs-on: ubuntu-latest
+    needs: set-matrix
+    strategy:
+      fail-fast: false
+      matrix:
+        # The version names here correspond to the versions of espressif/idf Docker image.
+        # See https://hub.docker.com/r/espressif/idf/tags and
+        # https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-docker-image.html
+        # for details.
+        idf_ver: ${{ fromJson(needs.set-matrix.outputs.idf_ver) }}
+        idf_target: ${{ fromJson(needs.set-matrix.outputs.idf_target) }}
+    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
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 00000000000..9b2c6bccab1
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,88 @@
+name: CodeQL Analysis
+
+on:
+  workflow_dispatch:
+  push:
+    branches:
+      - master
+  pull_request:
+    paths:
+      - "**/*.c"
+      - "**/*.cpp"
+      - "**/*.h"
+      - "**/*.ino"
+      - "**/*.py"
+      - ".github/workflows/*.yml"
+      - ".github/workflows/*.yaml"
+
+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 <Arduino.h>" > "$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/push.yml b/.github/workflows/push.yml
index 1523b7231be..48530e30bc9 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -25,34 +25,32 @@ 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/set_push_chunks.sh"
+      - ".github/scripts/sketch_utils.sh"
+      - "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}}
@@ -62,14 +60,6 @@ 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
@@ -78,7 +68,6 @@ jobs:
       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 }}
       chunk_count: ${{ steps.set-chunks.outputs.chunk_count }}
       chunks: ${{ steps.set-chunks.outputs.chunks }}
     steps:
@@ -99,13 +88,7 @@ jobs:
               - 'tools/**'
               - 'platform.txt'
               - 'programmers.txt'
-              - "variants/esp32/**/*"
-              - "variants/esp32c3/**/*"
-              - "variants/esp32c6/**/*"
-              - "variants/esp32h2/**/*"
-              - "variants/esp32p4/**/*"
-              - "variants/esp32s2/**/*"
-              - "variants/esp32s3/**/*"
+              - "variants/**"
             libraries:
               - 'libraries/**/examples/**'
               - 'libraries/**/src/**'
@@ -113,7 +96,7 @@ jobs:
               - 'libraries/Network/src/**'
             fs:
               - 'libraries/FS/src/**'
-            static_sketeches:
+            static_sketches:
               - 'libraries/NetworkClientSecure/examples/WiFiClientSecure/WiFiClientSecure.ino'
               - 'libraries/BLE/examples/Server/Server.ino'
               - 'libraries/ESP32/examples/Camera/CameraWebServer/CameraWebServer.ino'
@@ -121,11 +104,6 @@ jobs:
               - '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
@@ -133,9 +111,8 @@ jobs:
           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' }}
+          BUILD_STATIC_SKETCHES: ${{ steps.changed-files.outputs.static_sketches_any_changed == 'true' }}
           FS_CHANGED: ${{ steps.changed-files.outputs.fs_any_changed == 'true' }}
           NETWORKING_CHANGED: ${{ steps.changed-files.outputs.networking_any_changed == 'true' }}
           CORE_CHANGED: ${{ steps.changed-files.outputs.core_any_changed == 'true' }}
@@ -233,59 +210,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/tests.yml b/.github/workflows/tests.yml
index ddc9b64aace..4b915cf36de 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -16,22 +16,19 @@ 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 }}
@@ -48,9 +45,49 @@ jobs:
           name: event_file
           path: ${{ github.event_path }}
 
+  check-last-run:
+    name: Check last run
+    runs-on: ubuntu-latest
+    outputs:
+      run_tests: ${{ steps.last-run.outputs.run_tests }}
+    steps:
+      - name: Checkout
+        if: github.event_name != 'pull_request'
+        # if: github.event_name == 'schedule'
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          ref: gh-pages
+
+      - name: Get last run commit and compare with current commit
+        id: last-run
+        run: |
+          if [ ${{ github.event_name }} != 'pull_request' ]; then
+          # if [ ${{ github.event_name }} == 'schedule' ]; then
+            if [ -f ./runtime-test-results/test_results.json ]; then
+              last_run_commit=$(jq -r '.commit_sha' ./runtime-test-results/test_results.json)
+              last_run_failed=$(jq -r '.tests_failed' ./runtime-test-results/test_results.json)
+            else
+              last_run_commit="none"
+              last_run_failed="true"
+            fi
+            current_commit=${{ github.sha }}
+            if [ "$last_run_commit" == "$current_commit" ] && [ "$last_run_failed" == "false" ]; then
+              echo "Last run commit and current commit are the same. Skipping tests..."
+              echo "run_tests=false" >> $GITHUB_OUTPUT
+            else
+              echo "Last run commit and current commit are different or last run failed. Running tests..."
+              echo "run_tests=true" >> $GITHUB_OUTPUT
+            fi
+          else
+            echo "Not a schedule event. Continuing..."
+            echo "run_tests=true" >> $GITHUB_OUTPUT
+          fi
+
   gen-matrix:
     name: Generate matrix
     runs-on: ubuntu-latest
+    if: ${{ needs.check-last-run.outputs.run_tests }}
+    needs: check-last-run
     outputs:
       build-types: ${{ steps.set-matrix.outputs.build-types }}
       hw-types: ${{ steps.set-matrix.outputs.hw-types }}
diff --git a/.github/workflows/tests_build.yml b/.github/workflows/tests_build.yml
index ac1f40644ed..c74224941af 100644
--- a/.github/workflows/tests_build.yml
+++ b/.github/workflows/tests_build.yml
@@ -24,7 +24,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 +71,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 +81,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..e095ebe6dc8 100644
--- a/.github/workflows/tests_hw.yml
+++ b/.github/workflows/tests_hw.yml
@@ -39,7 +39,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 +61,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 +85,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 +103,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 +112,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..d9ddf6e1b0c 100644
--- a/.github/workflows/tests_qemu.yml
+++ b/.github/workflows/tests_qemu.yml
@@ -23,7 +23,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 +115,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 +127,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 +136,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..56883ecf52e 100644
--- a/.github/workflows/tests_results.yml
+++ b/.github/workflows/tests_results.yml
@@ -113,7 +113,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 +163,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_TESTS_REPORT.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 +180,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 +191,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..6af66cf0e42 100644
--- a/.github/workflows/tests_wokwi.yml
+++ b/.github/workflows/tests_wokwi.yml
@@ -135,17 +135,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 +231,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 +284,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 +299,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 +308,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/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..52554372ef4 100644
--- a/boards.txt
+++ b/boards.txt
@@ -41520,6 +41520,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=true
+sensebox_eye.upload.wait_for_upload_port=true
+
+sensebox_eye.serial.disableDTR=true
+sensebox_eye.serial.disableRTS=true
+
+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=default_16MB
+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.disabled=Disabled
+sensebox_eye.menu.PSRAM.disabled.build.defines=
+sensebox_eye.menu.PSRAM.disabled.build.psram_type=qspi
+sensebox_eye.menu.PSRAM.opi=OPI PSRAM
+sensebox_eye.menu.PSRAM.opi.build.defines=-DBOARD_HAS_PSRAM
+sensebox_eye.menu.PSRAM.opi.build.psram_type=opi
+
+sensebox_eye.menu.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.hwcdc=Hardware CDC and JTAG
+sensebox_eye.menu.USBMode.hwcdc.build.usb_mode=1
+sensebox_eye.menu.USBMode.default=USB-OTG (TinyUSB)
+sensebox_eye.menu.USBMode.default.build.usb_mode=0
+
+sensebox_eye.menu.CDCOnBoot.default=Enabled
+sensebox_eye.menu.CDCOnBoot.default.build.cdc_on_boot=1
+sensebox_eye.menu.CDCOnBoot.cdc=Disabled
+sensebox_eye.menu.CDCOnBoot.cdc.build.cdc_on_boot=0
+
+sensebox_eye.menu.MSCOnBoot.default=Disabled
+sensebox_eye.menu.MSCOnBoot.default.build.msc_on_boot=0
+sensebox_eye.menu.MSCOnBoot.msc=Enabled (Requires USB-OTG Mode)
+sensebox_eye.menu.MSCOnBoot.msc.build.msc_on_boot=1
+
+sensebox_eye.menu.DFUOnBoot.default=Disabled
+sensebox_eye.menu.DFUOnBoot.default.build.dfu_on_boot=0
+sensebox_eye.menu.DFUOnBoot.dfu=Enabled (Requires USB-OTG Mode)
+sensebox_eye.menu.DFUOnBoot.dfu.build.dfu_on_boot=1
+
+sensebox_eye.menu.UploadMode.default=UART0 / Hardware CDC
+sensebox_eye.menu.UploadMode.default.upload.use_1200bps_touch=false
+sensebox_eye.menu.UploadMode.default.upload.wait_for_upload_port=false
+sensebox_eye.menu.UploadMode.cdc=USB-OTG CDC (TinyUSB)
+sensebox_eye.menu.UploadMode.cdc.upload.use_1200bps_touch=true
+sensebox_eye.menu.UploadMode.cdc.upload.wait_for_upload_port=true
+
+sensebox_eye.menu.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.tinyuf2=TinyUF2 Compatibility (2MB APP/12MB FFAT)
+sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_bootloader=bootloader_tinyuf2
+sensebox_eye.menu.PartitionScheme.tinyuf2.build.custom_partitions=partitions_tinyuf2
+sensebox_eye.menu.PartitionScheme.tinyuf2.upload.maximum_size=2097152
+sensebox_eye.menu.PartitionScheme.tinyuf2.upload.extra_flags=0x410000 "{runtime.platform.path}/variants/{build.variant}/tinyuf2.bin"
+sensebox_eye.menu.PartitionScheme.gen4esp32scheme4=Huge App (16MB APP)
+sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.build.custom_partitions=gen4esp32_16MBapp
+sensebox_eye.menu.PartitionScheme.gen4esp32scheme4.upload.maximum_size=16646144
+
+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
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/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 <https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/esp32s2/sar-adc-bit1-no-flip.html>`_. 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/idf_component.yml b/idf_component.yml
index 82f14ea554a..f9357a401f9 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.6
     require: public
     rules:
       - if: "target not in [esp32c2, esp32p4]"
   espressif/esp-zigbee-lib:
-    version: "==1.6.5"
+    version: "==1.6.6"
     require: public
     rules:
       - if: "target not in [esp32c2, esp32p4]"
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/package/package_esp32_index.template.json b/package/package_esp32_index.template.json
index 3909c833c56..1b4b005dd34 100644
--- a/package/package_esp32_index.template.json
+++ b/package/package_esp32_index.template.json
@@ -81,7 +81,7 @@
             {
               "packager": "esp32",
               "name": "esptool_py",
-              "version": "5.0.0"
+              "version": "5.0.2"
             },
             {
               "packager": "esp32",
@@ -469,56 +469,56 @@
         },
         {
           "name": "esptool_py",
-          "version": "5.0.0",
+          "version": "5.0.2",
           "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.0.2/esptool-v5.0.2-linux-aarch64.tar.gz",
+              "archiveFileName": "esptool-v5.0.2-linux-aarch64.tar.gz",
+              "checksum": "SHA-256:0c1fa4f5e96f715133fa65572063fa65bd120cd941b667cecd3360436a62b97b",
+              "size": "57815944"
             },
             {
               "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.0.2/esptool-v5.0.2-linux-amd64.tar.gz",
+              "archiveFileName": "esptool-v5.0.2-linux-amd64.tar.gz",
+              "checksum": "SHA-256:519e0015872d527bdca850b18575d4f635fa88ccdffe47a14c99a80a90b780c5",
+              "size": "100787554"
             },
             {
               "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.0.2/esptool-v5.0.2-linux-armv7.tar.gz",
+              "archiveFileName": "esptool-v5.0.2-linux-armv7.tar.gz",
+              "checksum": "SHA-256:8df698d46a64b0b4a36d2a5bbd6bae58f81ca0a5e6451cd3f2d69a33340cc0f1",
+              "size": "53046401"
             },
             {
               "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.0.2/esptool-v5.0.2-macos-amd64.tar.gz",
+              "archiveFileName": "esptool-v5.0.2-macos-amd64.tar.gz",
+              "checksum": "SHA-256:5c27295975515b97a9280f46845bd3acd5fc9e6a0583cfe03efa66cc50195ab0",
+              "size": "59619952"
             },
             {
               "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.0.2/esptool-v5.0.2-macos-arm64.tar.gz",
+              "archiveFileName": "esptool-v5.0.2-macos-arm64.tar.gz",
+              "checksum": "SHA-256:93f0d9ef169f9bc6e32ed6f381977f63a5df7483b8002a5676dddf055bdbf775",
+              "size": "56344929"
             },
             {
               "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.0.2/esptool-v5.0.2-windows-amd64.zip",
+              "archiveFileName": "esptool-v5.0.2-windows-amd64.zip",
+              "checksum": "SHA-256:1caef993a16c5915714a0da772d93f2e3239f316c06223981b262b838287268c",
+              "size": "59097582"
             },
             {
               "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.0.2/esptool-v5.0.2-windows-amd64.zip",
+              "archiveFileName": "esptool-v5.0.2-windows-amd64.zip",
+              "checksum": "SHA-256:1caef993a16c5915714a0da772d93f2e3239f316c06223981b262b838287268c",
+              "size": "59097582"
             }
           ]
         },
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 <stdint.h>
+
+#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..4d6e38e73e0
--- /dev/null
+++ b/variants/sensebox_eye/variant.cpp
@@ -0,0 +1,12 @@
+#include "esp32-hal-gpio.h"
+#include "pins_arduino.h"
+
+extern "C" {
+
+void initVariant(void) {
+  // blink the RGB LED
+  rgbLedWrite(PIN_LED, 0x00, 0x10, 0x00);  // green
+  delay(20);
+  rgbLedWrite(PIN_LED, 0x00, 0x00, 0x00);  // off
+}
+}
diff --git a/variants/sensebox_mcu_esp32s2/APOTA.ino b/variants/sensebox_mcu_esp32s2/APOTA.ino
index 5f683752abc..67348d0b20f 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 <WiFi.h>
