Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions .github/workflows/firmware-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Firmware CI

on:
push:
paths:
- 'firmware/**'
- '.github/workflows/firmware-ci.yml'
pull_request:
paths:
- 'firmware/**'
- '.github/workflows/firmware-ci.yml'

jobs:
build:
name: Build ESP32-S3 Firmware
runs-on: ubuntu-latest
container:
image: espressif/idf:v5.2

steps:
- uses: actions/checkout@v4

- name: Build firmware
working-directory: firmware/esp32-csi-node
run: |
. $IDF_PATH/export.sh
idf.py set-target esp32s3
idf.py build

- name: Verify binary size (< 950 KB gate)
working-directory: firmware/esp32-csi-node
run: |
BIN=build/esp32-csi-node.bin
SIZE=$(stat -c%s "$BIN")
MAX=$((950 * 1024))
echo "Binary size: $SIZE bytes ($(( SIZE / 1024 )) KB)"
echo "Size limit: $MAX bytes (950 KB — includes Tier 3 WASM runtime)"
if [ "$SIZE" -gt "$MAX" ]; then
echo "::error::Firmware binary exceeds 950 KB size gate ($SIZE > $MAX)"
exit 1
fi
echo "Binary size OK: $SIZE <= $MAX"

- name: Verify flash image integrity
working-directory: firmware/esp32-csi-node
run: |
ERRORS=0
BIN=build/esp32-csi-node.bin

# Check binary exists and is non-empty.
if [ ! -s "$BIN" ]; then
echo "::error::Binary not found or empty"
exit 1
fi

# Check partition table magic (0xAA50 at offset 0).
PT=build/partition_table/partition-table.bin
if [ -f "$PT" ]; then
MAGIC=$(xxd -l2 -p "$PT")
if [ "$MAGIC" != "aa50" ]; then
echo "::warning::Partition table magic mismatch: $MAGIC (expected aa50)"
ERRORS=$((ERRORS + 1))
fi
fi

# Check bootloader exists.
BL=build/bootloader/bootloader.bin
if [ ! -s "$BL" ]; then
echo "::warning::Bootloader binary missing or empty"
ERRORS=$((ERRORS + 1))
fi

# Verify non-zero data in binary (not all 0xFF padding).
NONZERO=$(xxd -l 1024 -p "$BIN" | tr -d 'f' | wc -c)
if [ "$NONZERO" -lt 100 ]; then
echo "::error::Binary appears to be mostly padding (non-zero chars: $NONZERO)"
ERRORS=$((ERRORS + 1))
fi

if [ "$ERRORS" -gt 0 ]; then
echo "::warning::Flash image verification completed with $ERRORS warning(s)"
else
echo "Flash image integrity verified"
fi

- name: Check QEMU ESP32-S3 support status
run: |
echo "::notice::ESP32-S3 QEMU support is experimental in ESP-IDF v5.4. "
echo "Full smoke testing requires QEMU 8.2+ with xtensa-esp32s3 target."
echo "See: https://github.com/espressif/qemu/wiki"

- name: Upload firmware artifact
uses: actions/upload-artifact@v4
with:
name: esp32-csi-node-firmware
path: |
firmware/esp32-csi-node/build/esp32-csi-node.bin
firmware/esp32-csi-node/build/bootloader/bootloader.bin
firmware/esp32-csi-node/build/partition_table/partition-table.bin
retention-days: 30
14 changes: 13 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
# Local machine configuration (not shared)
# Local Claude config (contains WiFi credentials and machine-specific paths)
CLAUDE.local.md

# ESP32 firmware build artifacts and local config (contains WiFi credentials)
firmware/esp32-csi-node/build/
firmware/esp32-csi-node/sdkconfig
firmware/esp32-csi-node/sdkconfig.defaults
firmware/esp32-csi-node/sdkconfig.old
# Downloaded WASM3 source (fetched at configure time)
firmware/esp32-csi-node/components/wasm3/wasm3-src/

# NVS partition images and CSVs (contain WiFi credentials)
nvs.bin
nvs_config.csv
nvs_provision.bin

# Working artifacts that should not land in root
/*.wasm
/esp32_*.txt
/serial_error.txt

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
743 changes: 472 additions & 271 deletions README.md

Large diffs are not rendered by default.

Loading
Loading