Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
71 changes: 71 additions & 0 deletions .github/workflows/build_platformio.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: build_platformio

on: [push, pull_request]

jobs:
# ── Step 1: parse platformio.ini and produce the matrix list ──────────────
discover:
runs-on: ubuntu-latest
outputs:
envs: ${{ steps.get-envs.outputs.envs }}
steps:
- uses: actions/checkout@v4

- name: Discover buildable PlatformIO environments
id: get-envs
run: |
python3 - <<'EOF'
import re, json, os
with open("platformio.ini") as f:
content = f.read()
envs = re.findall(r"^\[env:([^\]]+)\]", content, re.MULTILINE)
# Skip abstract base templates and debug-only variants
envs = [e for e in envs if not e.startswith("base-") and not e.endswith("-debug")]
output = json.dumps(envs)
print(f"Discovered environments: {output}")
with open(os.environ["GITHUB_OUTPUT"], "a") as gh_out:
gh_out.write(f"envs={output}\n")
EOF

# ── Step 2: build each environment in parallel ────────────────────────────
build:
needs: discover
strategy:
fail-fast: false # let other envs finish even if one fails
matrix:
environment: ${{ fromJson(needs.discover.outputs.envs) }}

runs-on: ubuntu-latest
name: build / ${{ matrix.environment }}

steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- uses: actions/setup-python@v5
with:
python-version: "3.x"

# Cache the PlatformIO home directory (toolchains, platform packages)
# and the per-env build output so incremental builds are faster.
- uses: actions/cache@v4
with:
path: |
~/.platformio
.pio/build/${{ matrix.environment }}
key: pio-${{ matrix.environment }}-${{ hashFiles('platformio.ini', 'sdkconfig.defaults', 'sdkconfig.defaults.*') }}
restore-keys: |
pio-${{ matrix.environment }}-

- name: Install PlatformIO Core
run: pip install --quiet platformio

- name: Build ${{ matrix.environment }}
run: |
# On the first completely clean build, cmake configure runs *after*
# extra_scripts are evaluated, so pre_build.py finds no build.ninja
# and skips ESP-IDF embed-file stub generation → ninja fails.
# The second attempt finds the freshly created build.ninja and succeeds.
platformio run --environment ${{ matrix.environment }} \
|| platformio run --environment ${{ matrix.environment }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ build
libopus
managed_components
sdkconfig
sdkconfig.old
sdkconfig.*
!sdkconfig.defaults.*
dependencies.lock

# Created by https://www.toptal.com/developers/gitignore/api/c,c++,eclipse
Expand Down Expand Up @@ -149,3 +150,4 @@ local.properties

# End of https://www.toptal.com/developers/gitignore/api/c,c++,eclipse
.vscode-ctags
.vscode/c_cpp_properties.json
10 changes: 10 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}
44 changes: 44 additions & 0 deletions .vscode/launch.json
Comment thread
anabolyc marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY
//
// PlatformIO Debugging Solution
//
// Documentation: https://docs.platformio.org/en/latest/plus/debugging.html
// Configuration: https://docs.platformio.org/en/latest/projectconf/sections/env/options/debug/index.html

{
"version": "0.2.0",
"configurations": [
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug",
"executable": "/Users/andriy/prj/esparagus-media-center/esp32-snapclient/.pio/build/esparagus-audio-brick/firmware.elf",
"projectEnvName": "esparagus-audio-brick",
"toolchainBinDir": "/Users/andriy/.platformio/packages/toolchain-xtensa-esp-elf/bin",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": {
"type": "PlatformIO",
"task": "Pre-Debug"
}
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (skip Pre-Debug)",
"executable": "/Users/andriy/prj/esparagus-media-center/esp32-snapclient/.pio/build/esparagus-audio-brick/firmware.elf",
"projectEnvName": "esparagus-audio-brick",
"toolchainBinDir": "/Users/andriy/.platformio/packages/toolchain-xtensa-esp-elf/bin",
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "platformio-debug",
"request": "launch",
"name": "PIO Debug (without uploading)",
"executable": "/Users/andriy/prj/esparagus-media-center/esp32-snapclient/.pio/build/esparagus-audio-brick/firmware.elf",
"projectEnvName": "esparagus-audio-brick",
"toolchainBinDir": "/Users/andriy/.platformio/packages/toolchain-xtensa-esp-elf/bin",
"internalConsoleOptions": "openOnSessionStart",
"loadMode": "manual"
}
]
}
187 changes: 187 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
[platformio]
default_envs = esparagus-audio-brick
src_dir = main

; Common settings for all environments
[env]
monitor_speed = 115200
monitor_filters = direct
board_build.partitions = partitions.csv
extra_scripts = pre:pre_build.py

; ─── ESP32 base (40MHz/QIO/4MB) ─────────────────────────────────────────────
[env:base-esp32]
platform = platformio/espressif32 @ 6.12.0
framework = espidf
board = esp-wrover-kit
board_build.mcu = esp32
board_build.f_cpu = 240000000L
board_build.f_flash = 40000000L
board_build.flash_mode = qio
board_build.flash_size = 4MB
board_upload.flash_size = 4MB
monitor_rts = 0
monitor_dtr = 0

; ─── ESP32-S3 base (80MHz/QIO/4MB) ──────────────────────────────────────────
[env:base-esp32s3]
platform = platformio/espressif32 @ 6.12.0
framework = espidf
board = esp32-s3-devkitc-1
board_build.mcu = esp32s3
board_build.f_cpu = 240000000L
board_build.f_flash = 80000000L
board_build.flash_mode = qio
board_build.flash_size = 4MB
board_upload.flash_size = 4MB

; ─── HiFi-ESP32 (ESP32, PCM5100) ───────────────────────────────────────────
[env:hifi-esp32]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.hifi-esp32"

; ─── HiFi-ESP32-S3 (ESP32-S3, PCM5100) ───────────────────────────────────────────
[env:hifi-esp32s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.hifi-esp32s3"
Comment thread
anabolyc marked this conversation as resolved.
Outdated

; ─── HiFi-Esparagus (ESP32, PCM5100) ───────────────────────────────────────────
[env:hifi-esparagus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.hifi-esparagus"

; ─── HiFi-Esparagus-S3 (ESP32-S3, PCM5100) ───────────────────────────────────────────
[env:hifi-esparagus-s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.hifi-esparagus-s3"

; ─── HiFi-ESP32-Plus (ESP32, PCM5122) ───────────────────────────────────────────
[env:hifi-esp32-plus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.hifi-esp32-plus"

; ─── HiFi-ESP32-S3-Plus (ESP32-S3, PCM5122) ───────────────────────────────────────────
[env:hifi-esp32s3-plus]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.hifi-esp32s3-plus"

; ─── Loud-ESP32 (ESP32, MAX98357A) ───────────────────────────────────────────
[env:loud-esp32]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.loud-esp32"

; ─── Loud-ESP32-S3 (ESP32-S3, MAX98357A) ───────────────────────────────────────────
[env:loud-esp32s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.loud-esp32s3"

; ─── Loud-Esparagus (ESP32, MAX98357A) ───────────────────────────────────────────
[env:loud-esparagus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.loud-esparagus"

; ─── Esparagus-Echo (ESP32-S3, MAX98357A) ───────────────────────────────────────────
[env:esparagus-echo]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.esparagus-echo-s3"

; ─── Loud-ESP32-Plus (ESP32, MA12070P) ───────────────────────────────────────────
[env:loud-esp32-plus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.loud-esp32-plus"

; ─── Loud-ESP32-S3-Plus (ESP32-S3, MA12070P) ───────────────────────────────────────────
[env:loud-esp32-s3-plus]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.loud-esp32s3-plus"

; ─── Louder-ESP32 (ESP32, TAS5805M) ────────────────────────────────────────────────
[env:louder-esp32]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.louder-esp32"

; ─── Louder-ESP32-S3 (ESP32-S3, TAS5805M) ────────────────────────────────────────────────
[env:louder-esp32-s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.louder-esp32s3"

; ─── Louder-Esparagus (ESP32, TAS5805M) ────────────────────────────────────────────────
[env:louder-esparagus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.louder-esparagus"

; ─── Louder-ESP32-Plus (ESP32, TAS5825M) ────────────────────────────────────────────────
[env:louder-esp32-plus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.louder-esp32-plus"

; ─── Louder-ESP32-S3-Plus (ESP32-S3, TAS5825M) ────────────────────────────────────────────────
[env:louder-esp32-s3-plus]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.louder-esp32s3-plus"

; ─── Esparagus Audio Brick (ESP32, TAS5825M) ───────────────────────────────────────────
[env:esparagus-audio-brick]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.audio-brick-esp32"

; ─── Esparagus Audio Brick DEBUG (ESP32, TAS5825M) ─────────────────────────────────────
; [env:esparagus-audio-brick-debug]
; extends = env:esparagus-audio-brick
; board_build.cmake_extra_args =
; "-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.audio-brick-esp32;sdkconfig.defaults.static-wifi;sdkconfig.defaults.debug"
; monitor_filters = esp32_exception_decoder

; ─── Esparagus Audio Brick (ESP32-S3, TAS5825M) ────────────────────────────────────────
[env:esparagus-audio-brick-s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.audio-brick-esp32s3"

; ─── Amped-ESP32 (ESP32, PCM5100, TPA31XX amp) ───────────────────────────────────────────
[env:amped-esp32]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.amped-esp32"

; ─── Amped-ESP32-S3 (ESP32-S3, PCM510, TPA31XX amp) ───────────────────────────────────────────
[env:amped-esp32s3]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.amped-esp32s3"

; ─── Amped-Esparagus (ESP32, PCM5100, TPA31XX amp) ───────────────────────────────────────────
[env:amped-esparagus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.amped-esparagus"

; ─── Amped-ESP32-Plus (ESP32, PCM5122, TPA31XX amp) ───────────────────────────────────────────
[env:amped-esp32-plus]
extends = env:base-esp32
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults_psram;sdkconfig.defaults.amped-esp32-plus"

; ─── Amped-ESP32-S3-Plus (ESP32-S3, PCM5122, TPA31XX amp) ───────────────────────────────────────────
[env:amped-esp32s3-plus]
extends = env:base-esp32s3
board_build.cmake_extra_args =
"-DSDKCONFIG_DEFAULTS=sdkconfig.defaults;sdkconfig.defaults.esp32s3;sdkconfig.defaults.amped-esp32s3-plus"
58 changes: 58 additions & 0 deletions pre_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Pre-build script: generate EMBED_FILES / EMBED_TXTFILES assembly stubs.

ESP-IDF converts embedded binary/text files into assembly stubs (e.g.
favicon.ico.S) via CMake CUSTOM_COMMANDs. PlatformIO skips those commands
because it drives ninja directly on object-file targets. This script runs
`cmake --build . --target <name>.S` for every such stub before ninja starts,
so the source files exist when the assembler is invoked.
"""

import subprocess
import os
import re

Import("env") # noqa: F821 (PlatformIO injects this)


def generate_embedded_stubs():
build_dir = env.subst("$BUILD_DIR")
ninja_file = os.path.join(build_dir, "build.ninja")

if not os.path.isfile(ninja_file):
# build.ninja is generated by cmake configure, which runs *after*
# extra_scripts are evaluated on the very first clean build. Skip
# here — the CI workflow (and second local run) will pick it up.
print("pre_build.py: build.ninja not found (first clean build) — "
"skipping stub generation; retry the build to succeed.")
return

# Find all *.S targets produced by CUSTOM_COMMAND (the embed stubs).
# In build.ninja they appear as:
# build foo.ico.S | ...: CUSTOM_COMMAND ...
stub_pattern = re.compile(r"^build ([^|:\s]+\.S)(?:\s*\|[^:]*)?:\s*CUSTOM_COMMAND")
stubs = []
with open(ninja_file) as f:
for line in f:
m = stub_pattern.match(line)
if m:
stubs.append(m.group(1))

if not stubs:
return

for stub in stubs:
full_path = os.path.join(build_dir, stub)
if os.path.isfile(full_path):
continue # already generated, skip
print(f"pre_build.py: generating {stub} ...")
result = subprocess.run(
["cmake", "--build", ".", "--target", stub],
cwd=build_dir,
)
if result.returncode != 0:
print(f"pre_build.py: WARNING — cmake failed to generate {stub} "
f"(exit {result.returncode})")


generate_embedded_stubs()
Loading
Loading