Detect a camera on a randomised MAC, and make the fingerprint able to… #237
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: "ESP32: Build companion firmware" | |
| # Downloading an esp32 core is heavy, so the RELEASE build stays tag-only. | |
| # The core-3.x compat job, however, runs on every push and PR: it exists because | |
| # a tag-only compile is exactly what let issue #4 ship (the sketch did not build | |
| # on the core new users install, and CI never once tried). | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["v*"] | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: write # attach the firmware image to the tag's Release | |
| # This is the slowest workflow in the repo (an esp32 core download plus three | |
| # compiles), so superseded runs are the ones that clog the queue. PR #18 pushed | |
| # twice in quick succession and the first run sat QUEUED FOR 25 HOURS behind its | |
| # own replacement, on a branch whose result nobody was waiting for any more. | |
| # | |
| # Tags are exempt from cancellation on purpose: a cancelled tag run never | |
| # attaches the companion firmware, and the release is then missing the largest | |
| # download with nothing red to show for it. Tags are pushed once and superseding | |
| # one is not a thing that happens. | |
| concurrency: | |
| group: esp32-${{ github.ref }} | |
| cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }} | |
| jobs: | |
| build-bin: | |
| # Release artefact only: tags and manual runs. Day-to-day portability is | |
| # covered by build-core3 below. | |
| if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| name: "Build flock_companion .bin" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| # Nothing after this checkout runs an authenticated git command. | |
| persist-credentials: false | |
| - name: Setup arduino-cli | |
| uses: arduino/setup-arduino-cli@81d310742121c928ea9c8bbd407b4217b432ae02 # v2.0.0 | |
| - name: Install ESP32 core | |
| run: | | |
| # RETRY THE INDEX FETCH. downloads.arduino.cc reset the connection | |
| # mid-release on 2026-08-30 ("Download failed: performing HEAD request | |
| # ... read: connection reset by peer", then "Some indexes could not be | |
| # updated"). That failed the C5 job, which meant its asset never | |
| # attached, which made the SHA256SUMS job correctly refuse to publish a | |
| # manifest with a missing file -- so one dropped TCP connection took out | |
| # the whole release and needed a human to notice and re-run it. | |
| # | |
| # Nothing about that failure was our code, and nothing about it was | |
| # worth a red release. Three attempts with a backoff, and the step still | |
| # fails loudly if the index is genuinely unreachable. | |
| retry() { | |
| local n=1 | |
| until "$@"; do | |
| if [ "$n" -ge 3 ]; then | |
| echo "::error::failed after $n attempts: $*" | |
| return 1 | |
| fi | |
| echo "attempt $n failed: $* -- retrying in $((n * 10))s" | |
| sleep $((n * 10)) | |
| n=$((n + 1)) | |
| done | |
| } | |
| arduino-cli config init --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json | |
| retry arduino-cli core update-index | |
| # 2.0.17 is what the RELEASED WROOM image is built from -- the version | |
| # with the most field mileage. Portability against core 3.x is proven | |
| # by the separate compat job below, not by moving this pin. | |
| retry arduino-cli core install esp32:esp32@2.0.17 | |
| - name: Compile (classic ESP32 / WROOM) | |
| run: | | |
| # huge_app: WiFi + BLE firmware exceeds the default 1.3 MB app partition. | |
| arduino-cli compile \ | |
| --fqbn esp32:esp32:esp32:PartitionScheme=huge_app \ | |
| --output-dir build_esp \ | |
| esp32_companion/flock_companion | |
| # Bench-only test target (tools/flock_emitter). Compile-checked here so it | |
| # cannot rot silently against the signature tables it impersonates, but | |
| # deliberately NOT merged or attached to a release: it transmits, and | |
| # nothing about FlipDeFlock should make it easy to flash by accident. | |
| - name: Compile bench emitter (not released) | |
| run: | | |
| arduino-cli compile \ | |
| --fqbn esp32:esp32:esp32:PartitionScheme=huge_app \ | |
| --output-dir build_emitter \ | |
| tools/flock_emitter | |
| # Single image flashable whole at 0x0 -- what FlipDeFlock's in-app flasher | |
| # writes. | |
| # | |
| # Offsets are explicit here rather than read from the core's flash_args | |
| # (as the C5 step does) because THIS job pins core 2.0.17, and flash_args | |
| # is a 3.x-only output -- verified absent from 2.0.x platform.txt. These | |
| # values are the classic-ESP32 layout and have shipped since v0.44. | |
| - name: Merge into a single flashable image | |
| run: | | |
| python -m pip install --quiet esptool | |
| BOOT0=$(find "$HOME/.arduino15" -name boot_app0.bin | head -n1) | |
| python -m esptool --chip esp32 merge_bin -o build_esp/flipdeflock_companion_esp32wroom.bin \ | |
| 0x1000 build_esp/flock_companion.ino.bootloader.bin \ | |
| 0x8000 build_esp/flock_companion.ino.partitions.bin \ | |
| 0xe000 "$BOOT0" \ | |
| 0x10000 build_esp/flock_companion.ino.bin | |
| # esptool's merge_bin spans lowest offset -> end of last chunk without | |
| # --pad-to-size, so this image is already unpadded. Assert it, so a | |
| # future esptool default change cannot silently reintroduce 4 MB of | |
| # 0xFF that the Flipper would dutifully write over UART. | |
| python - <<'PY' | |
| import sys, pathlib | |
| p = pathlib.Path("build_esp/flipdeflock_companion_esp32wroom.bin") | |
| d = p.read_bytes() | |
| assert d[0x1000] == 0xE9, f"bootloader magic missing at 0x1000 (got {d[0x1000]:#04x})" | |
| assert len(d) < 3_000_000, f"image looks padded to flash size: {len(d):,} bytes" | |
| print(f"WROOM image OK: {len(d):,} bytes, bootloader magic at 0x1000") | |
| PY | |
| # ESP32-S2 (the official Flipper Wi-Fi Devboard / Wi-Fi Module v1). That chip | |
| # has NO Bluetooth radio, so the core ships no BLE library and the sketch | |
| # would not compile for it at all -- reported as issue #25, where the board | |
| # flashed "fine" and then sat at ch 0 forever with no error. It now builds | |
| # WI-FI ONLY via FLOCK_HAS_BLE. Built here so the target cannot rot, and | |
| # released, because "buy a different board" is a poor answer to someone who | |
| # already owns the official one. | |
| # | |
| # This image is DEGRADED BY DESIGN: probe/OUI detection works, the BLE half | |
| # of Flock detection is permanently absent. The app says "WiFi only" when it | |
| # sees this SoC, and the README says why. | |
| - name: Compile (ESP32-S2, Wi-Fi only) | |
| run: | | |
| arduino-cli compile --fqbn esp32:esp32:esp32s2:PartitionScheme=huge_app --output-dir build_s2 esp32_companion/flock_companion | |
| - name: Merge S2 into a single flashable image | |
| run: | | |
| BOOT0=$(find "$HOME/.arduino15" -name boot_app0.bin | head -n1) | |
| python -m esptool --chip esp32s2 merge_bin -o build_s2/flipdeflock_companion_esp32s2.bin 0x1000 build_s2/flock_companion.ino.bootloader.bin 0x8000 build_s2/flock_companion.ino.partitions.bin 0xe000 "$BOOT0" 0x10000 build_s2/flock_companion.ino.bin | |
| python - <<'PY' | |
| import pathlib | |
| p = pathlib.Path("build_s2/flipdeflock_companion_esp32s2.bin") | |
| d = p.read_bytes() | |
| assert d[0x1000] == 0xE9, f"bootloader magic missing at 0x1000 (got {d[0x1000]:#04x})" | |
| assert len(d) < 3_000_000, f"image looks padded to flash size: {len(d):,} bytes" | |
| print(f"S2 image OK: {len(d):,} bytes, bootloader magic at 0x1000") | |
| PY | |
| - name: Upload firmware binaries | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: flock_companion-esp32-bin | |
| path: | | |
| build_esp/flock_companion.ino.bin | |
| build_esp/flock_companion.ino.bootloader.bin | |
| build_esp/flock_companion.ino.partitions.bin | |
| build_esp/flipdeflock_companion_esp32wroom.bin | |
| build_s2/flipdeflock_companion_esp32s2.bin | |
| if-no-files-found: error | |
| # On a tag, attach the full 0x0-flashable image to that tag's Release so | |
| # users can download the companion firmware directly (not just from CI | |
| # artifacts). Mirrors release.yml's resilient softprops attach: it updates | |
| # an existing release for the tag and won't fail the run on a miss. | |
| - name: Attach companion firmware to GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 | |
| with: | |
| files: | | |
| build_esp/flipdeflock_companion_esp32wroom.bin | |
| build_s2/flipdeflock_companion_esp32s2.bin | |
| fail_on_unmatched_files: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # --------------------------------------------------------------------------- | |
| # Arduino core 3.x compatibility + the ESP32-C5 dual-band target. | |
| # | |
| # WHY THIS JOB EXISTS: the build above pins core 2.0.17, so for months nothing | |
| # ever compiled the sketch against core 3.x -- which is what a fresh Arduino | |
| # install has actually been getting. The result was that the companion did not | |
| # build AT ALL for new users (issue #4, @h00die), and CI was green throughout. | |
| # A pin without a matching compat build is a blind spot, not a policy. | |
| # | |
| # Runs on every push/PR, not just tags, because this is a regression guard. | |
| build-core3: | |
| runs-on: ubuntu-latest | |
| name: "Core 3.x compat + ESP32-C5" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| # Nothing after this checkout runs an authenticated git command. | |
| persist-credentials: false | |
| - name: Setup arduino-cli | |
| uses: arduino/setup-arduino-cli@81d310742121c928ea9c8bbd407b4217b432ae02 # v2.0.0 | |
| - name: Install ESP32 core 3.x | |
| run: | | |
| # RETRY THE INDEX FETCH. downloads.arduino.cc reset the connection | |
| # mid-release on 2026-08-30 ("Download failed: performing HEAD request | |
| # ... read: connection reset by peer", then "Some indexes could not be | |
| # updated"). That failed the C5 job, which meant its asset never | |
| # attached, which made the SHA256SUMS job correctly refuse to publish a | |
| # manifest with a missing file -- so one dropped TCP connection took out | |
| # the whole release and needed a human to notice and re-run it. | |
| # | |
| # Nothing about that failure was our code, and nothing about it was | |
| # worth a red release. Three attempts with a backoff, and the step still | |
| # fails loudly if the index is genuinely unreachable. | |
| retry() { | |
| local n=1 | |
| until "$@"; do | |
| if [ "$n" -ge 3 ]; then | |
| echo "::error::failed after $n attempts: $*" | |
| return 1 | |
| fi | |
| echo "attempt $n failed: $* -- retrying in $((n * 10))s" | |
| sleep $((n * 10)) | |
| n=$((n + 1)) | |
| done | |
| } | |
| arduino-cli config init --additional-urls https://espressif.github.io/arduino-esp32/package_esp32_index.json | |
| retry arduino-cli core update-index | |
| retry arduino-cli core install esp32:esp32 | |
| # Classic ESP32 on core 3.x: proves the 2.x/3.x shims work and that the | |
| # 5 GHz code is genuinely compiled out on a 2.4-only radio. | |
| - name: Compile classic ESP32 (core 3.x) | |
| run: | | |
| arduino-cli compile \ | |
| --fqbn esp32:esp32:esp32:PartitionScheme=huge_app \ | |
| esp32_companion/flock_companion | |
| - name: Compile bench emitter (core 3.x) | |
| run: | | |
| arduino-cli compile \ | |
| --fqbn esp32:esp32:esp32:PartitionScheme=huge_app \ | |
| tools/flock_emitter | |
| # ESP32-C5: the dual-band target. The bootloader offset differs (0x2000, | |
| # not the classic 0x1000), so the merge is driven by the core's flash_args | |
| # rather than hardcoded numbers -- a wrong offset does not fail loudly, it | |
| # boot-loops the board with `invalid header`. | |
| # | |
| # Deliberately NOT the core's own *.merged.bin: that is padded out to the | |
| # full 4 MB flash size for ~1.4 MB of firmware, and the Flipper's flasher | |
| # writes every byte of it (no skip-blank), so the padding is pure UART time | |
| # for anyone flashing from the Flipper. | |
| # --build-path is load-bearing, not tidiness: the core writes flash_args | |
| # into the BUILD directory, and --output-dir receives only a subset of the | |
| # build products (flash_args is not among them on Linux). Pinning the build | |
| # path is what makes the offsets readable at all. | |
| - name: Compile ESP32-C5 (dual-band, UNVERIFIED on hardware) | |
| run: | | |
| arduino-cli compile \ | |
| --fqbn esp32:esp32:esp32c5:PartitionScheme=huge_app \ | |
| --build-path "$PWD/build_c5_raw" \ | |
| --output-dir build_c5 \ | |
| esp32_companion/flock_companion | |
| python -m pip install --quiet esptool | |
| python tools/merge_esp_image.py \ | |
| build_c5_raw esp32c5 build_c5/flipdeflock_companion_esp32c5_EXPERIMENTAL.bin | |
| - name: Upload C5 firmware (experimental) | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: flock_companion-esp32c5-experimental | |
| path: build_c5/flipdeflock_companion_esp32c5_EXPERIMENTAL.bin | |
| if-no-files-found: error | |
| # Attached to the release with an EXPERIMENTAL name on purpose: it is | |
| # compile-verified only. Nobody on the project owns a C5, so no one has | |
| # confirmed it boots, hops, or detects anything. The filename is the | |
| # warning that travels with the download. | |
| - name: Attach C5 firmware to GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/') | |
| uses: softprops/action-gh-release@efb35369e0ad2afab669f228072c1b0d510eae64 # v3.0.3 | |
| with: | |
| files: build_c5/flipdeflock_companion_esp32c5_EXPERIMENTAL.bin | |
| fail_on_unmatched_files: false | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |