Files
klubhaus-doorbell/libraries/FastLED/ci/boards.py
2026-02-12 00:45:31 -08:00

770 lines
25 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dataclasses
import json
from copy import deepcopy
from dataclasses import dataclass
from typing import Any
# An open source version of the esp-idf 5.1 platform for the ESP32 that
# gives esp32 boards the same build environment as the Arduino 2.3.1+.
# Set to a specific release, we may want to update this in the future.
ESP32_IDF_5_1_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/51.03.04/platform-espressif32.zip"
# TODO: Upgrade toolkit to 5.3
ESP32_IDF_5_3_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10/platform-espressif32.zip"
ESP32_IDF_5_4_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/54.03.20/platform-espressif32.zip"
ESP32_IDF_5_5_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32/releases/download/55.03.30-2/platform-espressif32.zip"
ESP32_IDF_LATEST_PIOARDUINO = ESP32_IDF_5_5_PIOARDUINO
ESP32_IDF_LATEST_PIOARDUINO = (
"https://github.com/pioarduino/platform-espressif32.git#develop"
)
ESP32_IDF_4_4_LATEST = (
"https://github.com/platformio/platform-espressif32/archive/refs/tags/v4.4.0.zip"
)
APOLLO3_2_2_0 = "https://github.com/nigelb/platform-apollo3blue"
# Top of trunk.
# ESP32_IDF_5_1_PIOARDUINO = "https://github.com/pioarduino/platform-espressif32"
# Old fork that we were using
# ESP32_IDF_5_1_PIOARDUINO = "https://github.com/zackees/platform-espressif32#Arduino/IDF5"
# ALL will be auto populated in the Board constructor whenever a
# board is defined.
ALL: list["Board"] = []
class Auto:
pass
@dataclass
class Board:
board_name: str
real_board_name: str | None = None
platform: str | None = None
platform_needs_install: bool = False
use_pio_run: bool = (
False # some platforms like esp32-c2-devkitm-1 will only work with pio run
)
platform_packages: str | None = None
framework: str | None = None
board_build_mcu: str | None = None
board_build_core: str | None = None
board_build_filesystem_size: str | None = None
build_flags: list[str] | None = None # Reserved for future use.
build_unflags: list[str] | None = None # New: unflag options
defines: list[str] | None = None
customsdk: str | None = None
board_partitions: str | None = None # Reserved for future use.
no_board_spec: bool = (
False # For platforms like 'native' that don't need a board specification
)
add_board_to_all: bool = True
lib_compat_mode: str | None = (
None # Library compatibility mode (e.g., 'off' for native platform)
)
lib_ldf_mode: str | None = (
None # Library Dependency Finder mode (e.g., 'chain+' for enhanced dependency finding)
)
lib_ignore: list[str] | None = (
None # Libraries to ignore during compilation (e.g., ['I2S'] for UNO R4 WiFi)
)
def __post_init__(self) -> None:
# Check if framework is set, warn and auto-set to arduino if missing (except for native/stub platforms)
if self.framework is None and not self._is_native_or_stub_platform():
self.framework = "arduino"
# Auto-detect and exclude problematic libraries based on environmental signals
self._auto_detect_library_exclusions()
ALL.append(self)
def _is_native_or_stub_platform(self) -> bool:
"""Check if this is a native or stub platform that doesn't need a framework"""
# Check for native platform
if self.platform and (
"native" in self.platform.lower() or "stub" in self.platform.lower()
):
return True
# Check for stub build flags
if self.build_flags:
for flag in self.build_flags:
if "FASTLED_STUB" in flag or "PLATFORM_NATIVE" in flag:
return True
# Check no_board_spec flag (typically used for native platforms)
if self.no_board_spec:
return True
return False
def _auto_detect_library_exclusions(self) -> None:
"""Automatically detect and exclude libraries known to be broken on this platform."""
# Check for I2S library problems based on environmental signals
if self._should_ignore_i2s_library():
if not self.lib_ignore:
self.lib_ignore = []
if "I2S" not in self.lib_ignore:
self.lib_ignore.append("I2S")
def _should_ignore_i2s_library(self) -> bool:
"""Detect if I2S library should be ignored based on platform characteristics."""
# Known problematic platforms - Renesas RA family
if self.platform == "renesas-ra":
return True
# Known problematic board names
problematic_boards = ["uno_r4_wifi", "uno_r4_minima"]
if self.board_name in problematic_boards:
return True
# Check for specific defines that indicate I2S problems
if self.defines:
problematic_defines = [
"ARDUINO_UNOR4_WIFI",
"ARDUINO_UNOR4_MINIMA",
"ARDUINO_ARCH_RENESAS",
"ARDUINO_ARCH_RENESAS_UNO",
"_RENESAS_RA_",
"ARDUINO_FSP",
]
for define in self.defines:
# Check if define contains any problematic patterns
if any(prob_define in define for prob_define in problematic_defines):
return True
return False
def clone(self) -> "Board":
out = Board(
board_name=self.board_name,
add_board_to_all=False,
)
for field_name, field_info in self.__dataclass_fields__.items():
field_value: Any = getattr(self, field_name)
# Create deep copy for mutable types to avoid shared references
if isinstance(field_value, (list, dict)):
field_value = deepcopy(field_value) # type: ignore[misc]
setattr(out, field_name, field_value)
return out
def get_real_board_name(self) -> str:
return self.real_board_name if self.real_board_name else self.board_name
def to_dictionary(self) -> dict[str, list[str]]:
out: dict[str, list[str]] = {}
if self.real_board_name:
out[self.board_name] = [f"board={self.real_board_name}"]
options = out.setdefault(self.board_name, [])
if self.platform:
options.append(f"platform={self.platform}")
if self.platform_needs_install:
options.append("platform_needs_install=true")
if self.platform_packages:
options.append(f"platform_packages={self.platform_packages}")
if self.framework:
options.append(f"framework={self.framework}")
if self.board_build_core:
options.append(f"board_build.core={self.board_build_core}")
if self.board_build_mcu:
options.append(f"board_build.mcu={self.board_build_mcu}")
if self.board_build_filesystem_size:
options.append(
f"board_build.filesystem_size={self.board_build_filesystem_size}"
)
if self.defines:
for define in self.defines:
options.append(f"build_flags=-D{define}")
# Handle build_unflags
if self.build_unflags:
for uf in self.build_unflags:
options.append(f"build_unflags={uf}")
# Handle explicit build_flags (added for native host compilation and other special cases)
if self.build_flags:
for bf in self.build_flags:
options.append(f"build_flags={bf}")
if self.customsdk:
options.append(f"custom_sdkconfig={self.customsdk}")
# Add board-specific build cache directory pointing via symlink directive
# here = Path(__file__).parent
# project_root = here.parent.parent # Move from ci/util/ to project root
# cache_dir = project_root / ".pio_cache" / self.board_name
# absolute_cache_dir = cache_dir.resolve()
# options.append(f"build_cache_dir=symlink://{absolute_cache_dir}")
return out
def __repr__(self) -> str:
json_str = json.dumps(self.to_dictionary(), indent=4, sort_keys=True)
return json_str
def __hash__(self) -> int:
data_str = self.__repr__()
return hash(data_str)
def to_platformio_ini(
self,
additional_defines: list[str] | None = None,
additional_include_dirs: list[str] | None = None,
additional_libs: list[str] | None = None,
include_platformio_section: bool = False,
core_dir: str | None = None,
packages_dir: str | None = None,
project_root: str | None = None,
build_cache_dir: str | None = None,
extra_scripts: list[str] | None = None,
) -> str:
"""Return a `platformio.ini` snippet representing this board.
The output is suitable for directly appending to a *platformio.ini* file
and follows the same semantics used by the PlatformIO CLI. Only
parameters understood by PlatformIO are emitted internal helper
fields like ``platform_needs_install`` and ``use_pio_run`` are **not**
included because they are consumed exclusively by the build helpers in
the *ci/* folder and would be ignored (or flagged as errors) by
PlatformIO itself.
Args:
example: Example name for dynamic build flags (currently unused)
additional_defines: Additional defines to merge with board defines
additional_include_dirs: Additional include directories to merge with build flags
include_platformio_section: Whether to include [platformio] section with core_dir/packages_dir
core_dir: PlatformIO core directory path
packages_dir: PlatformIO packages directory path
project_root: FastLED project root for lib_deps symlink
"""
lines: list[str] = []
# Optional [platformio] section
if include_platformio_section:
lines.append("[platformio]")
if build_cache_dir:
lines.append(f"build_cache_dir = {build_cache_dir}")
if core_dir:
lines.append(f"core_dir = {core_dir}")
if packages_dir:
lines.append(f"packages_dir = {packages_dir}")
lines.append("")
# Section header
lines.append(f"[env:{self.board_name}]")
if extra_scripts:
lines.append(f"extra_scripts = {' '.join(extra_scripts)}")
# Board identifier (skip for platforms that don't need board specification)
if not self.no_board_spec:
lines.append(f"board = {self.get_real_board_name()}")
# Optional parameters -------------------------------------------------
if self.platform:
lines.append(f"platform = {self.platform}")
if self.platform_packages:
lines.append(f"platform_packages = {self.platform_packages}")
if self.framework:
lines.append(f"framework = {self.framework}")
if self.board_build_core:
lines.append(f"board_build.core = {self.board_build_core}")
if self.board_build_mcu:
lines.append(f"board_build.mcu = {self.board_build_mcu}")
if self.board_build_filesystem_size:
lines.append(
f"board_build.filesystem_size = {self.board_build_filesystem_size}"
)
if self.board_partitions:
lines.append(f"board_partitions = {self.board_partitions}")
# Build-time flags and unflags ---------------------------------------
build_flags_elements: list[str] = []
if self.defines:
build_flags_elements.extend(f"-D{define}" for define in self.defines)
# Add additional defines
if additional_defines:
build_flags_elements.extend(f"-D{define}" for define in additional_defines)
# Use static build flags
if self.build_flags:
build_flags_elements.extend(self.build_flags)
# Add additional include directories
if additional_include_dirs:
build_flags_elements.extend(
f"-I{include_dir}" for include_dir in additional_include_dirs
)
if build_flags_elements:
# Put each build flag on a separate line for better readability and debugging
lines.append("build_flags =")
for flag in build_flags_elements:
lines.append(f" {flag}")
if self.build_unflags:
# PlatformIO accepts multiple *build_unflags* separated by spaces.
# Emit a single line for readability.
lines.append(f"build_unflags = {' '.join(self.build_unflags)}")
# Custom ESP-IDF sdkconfig override (ESP32-family boards)
if self.customsdk:
lines.append(f"custom_sdkconfig = {self.customsdk}")
# Library compatibility mode (for platforms like native that need special handling)
if self.lib_compat_mode:
lines.append(f"lib_compat_mode = {self.lib_compat_mode}")
# Library Dependency Finder mode (for enhanced dependency finding)
if self.lib_ldf_mode:
lines.append(f"lib_ldf_mode = {self.lib_ldf_mode}")
# Libraries to ignore during compilation
if self.lib_ignore:
if len(self.lib_ignore) == 1:
lines.append(f"lib_ignore = {self.lib_ignore[0]}")
else:
lines.append("lib_ignore =")
for lib in self.lib_ignore:
lines.append(f" {lib}")
# Add FastLED-specific configurations if project_root is provided
if project_root:
# Only add default lib_ldf_mode if board doesn't specify its own
current_config = "\n".join(lines)
if "lib_ldf_mode" not in current_config:
lines.append("lib_ldf_mode = chain")
lines.append("lib_archive = true")
# Build lib_deps with additional libs only (FastLED is copied to lib/FastLED)
# PlatformIO supports multiple lib_deps entries on separate lines
if additional_libs:
# Format as multi-line lib_deps for proper PlatformIO parsing
if len(additional_libs) == 1:
lines.append(f"lib_deps = {additional_libs[0]}")
else:
lines.append("lib_deps =")
for entry in additional_libs:
lines.append(f" {entry}")
return "\n".join(lines) + "\n"
# [env:sparkfun_xrp_controller]
# platform = https://github.com/maxgerhardt/platform-raspberrypi
# board = sparkfun_xrp_controller
# framework = arduino
# lib_deps = fastled/FastLED @ ^3.9.16
WEBTARGET = Board(
board_name="web",
)
# Native host compilation target using PlatformIO's "native" platform.
# This allows compiling FastLED for the host machine (Linux/macOS/Windows)
# which is useful for CI compile-tests and static analysis. We replicate
# the build flags present in ci/native/platformio.ini so that the same
# stub implementation and main-file inclusion are used.
NATIVE = Board(
board_name="native",
platform="platformio/native",
no_board_spec=True, # Native platform doesn't need a board specification
lib_compat_mode="off", # Disable library compatibility checking for native platform
build_flags=[
"-DFASTLED_STUB_IMPL", # Enable stub platform implementations
"-DFASTLED_USE_STUB_ARDUINO", # Enable Arduino stub implementations
"-DPLATFORM_NATIVE", # Enable native platform stub compilation
"-std=c++17",
"-I../../../src/platforms/stub", # Include path for Arduino.h and other stub headers (relative to project dir)
"-I../../../src", # Include path for FastLED.h and other source headers (relative to project dir)
],
)
DUE = Board(
board_name="due",
platform="atmelsam",
)
SPARKFUN_XRP_CONTROLLER_2350B = Board(
board_name="sparkfun_xrp_controller",
platform="https://github.com/maxgerhardt/platform-raspberrypi",
platform_needs_install=True,
)
APOLLO3_RED_BOARD = Board(
board_name="apollo3_red",
real_board_name="SparkFun_RedBoard_Artemis_ATP",
platform=APOLLO3_2_2_0,
platform_packages="framework-arduinoapollo3@https://github.com/sparkfun/Arduino_Apollo3#v2.2.0",
platform_needs_install=True,
)
APOLLO3_SPARKFUN_THING_PLUS_EXPLORABLE = Board(
board_name="apollo3_thing_explorable",
real_board_name="SparkFun_Thing_Plus_expLoRaBLE",
platform=APOLLO3_2_2_0,
platform_packages="framework-arduinoapollo3@https://github.com/sparkfun/Arduino_Apollo3#v2.2.0",
platform_needs_install=True,
)
ESP32DEV = Board(
board_name="esp32dev",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32DEV_IDF3_3 = Board(
board_name="esp32dev_idf33",
real_board_name="esp32dev",
platform="espressif32@1.11.2",
)
ESP32DEV_IDF4_4 = Board(
board_name="esp32dev_idf44",
real_board_name="esp32dev",
platform=ESP32_IDF_4_4_LATEST,
)
GIGA_R1 = Board(
board_name="giga_r1",
platform="ststm32",
framework="arduino",
real_board_name="giga_r1_m7",
)
# ESP01 = Board(
# board_name="esp01",
# platform=ESP32_IDF_5_1_PIOARDUINO,
# )
ESP32_C2_DEVKITM_1 = Board(
board_name="esp32c2",
real_board_name="esp32-c2-devkitm-1",
platform="https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip",
framework="arduino, espidf",
)
ESP32_C3_DEVKITM_1 = Board(
board_name="esp32c3",
real_board_name="esp32-c3-devkitm-1",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_C5_DEVKITC_1 = Board(
board_name="esp32c5",
real_board_name="esp32-c5-devkitc-1",
platform=ESP32_IDF_5_5_PIOARDUINO,
)
ESP32_C6_DEVKITC_1 = Board(
board_name="esp32c6",
real_board_name="esp32-c6-devkitc-1",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_S3_DEVKITC_1 = Board(
board_name="esp32s3",
real_board_name="esp32-s3-devkitc-1",
platform=ESP32_IDF_5_4_PIOARDUINO,
framework="arduino",
board_partitions="huge_app.csv",
build_unflags=["-DFASTLED_RMT5=0", "-DFASTLED_RMT5"],
)
ESP32_S2_DEVKITM_1 = Board(
board_name="esp32s2",
real_board_name="lolin_s2_mini ",
board_build_mcu="esp32s2",
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_UPESY_WROOM = Board(
board_name="upesy_wroom",
real_board_name="upesy_wroom",
platform="espressif32",
)
ESP32H2 = Board(
board_name="esp32h2",
real_board_name="esp32-h2-devkitm-1",
platform_needs_install=True, # Install platform package to get the boards
platform=ESP32_IDF_5_3_PIOARDUINO,
)
ESP32_P4 = Board(
board_name="esp32p4",
real_board_name="esp32-p4-evboard",
platform_needs_install=True, # Install platform package to get the boards
platform="https://github.com/pioarduino/platform-espressif32/releases/download/stable/platform-espressif32.zip",
)
ADA_FEATHER_NRF52840_SENSE = Board(
board_name="adafruit_feather_nrf52840_sense",
platform="nordicnrf52",
)
XIAOBLESENSE_ADAFRUIT_NRF52 = Board(
board_name="xiaoblesense_adafruit",
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True, # Install platform package to get the boards
)
# Alias: handle common misspelling without the trailing 't'
XIAOBLESENSE_ADAFRUI_ALIAS = Board(
board_name="xiaoblesense_adafrui", # missing 't'
real_board_name="xiaoblesense_adafruit", # map to the correct board name
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True,
)
XIAOBLESENSE_NRF52 = Board(
board_name="xiaoblesense",
real_board_name="xiaoble_adafruit",
platform="https://github.com/maxgerhardt/platform-nordicnrf52",
platform_needs_install=True,
)
# Correct nRF52840 DK board definition
# The Nordic nRF52840 DK is directly supported by the default PlatformIO
# `nordicnrf52` platform under the board name `nrf52840_dk`, so we don't
# need a custom platform package or extra installation steps. Point the
# Board definition at the stock platform and use the canonical board name.
# This fixes compilation failures introduced during the build-system
# migration where the board was temporarily mapped to the XIAO variant.
NRF52840 = Board(
board_name="nrf52840_dk",
real_board_name="nrf52840_dk_adafruit", # Use Adafruit BSP variant which includes full Nordic SDK headers
platform="nordicnrf52",
framework="arduino",
platform_needs_install=False,
platform_packages="framework-arduinoadafruitnrf52@^1.10601.0",
defines=[
"FASTLED_USE_COMPILE_TESTS=0",
],
board_build_core="nRF5", # Ensure correct core directory
)
RPI_PICO = Board(
board_name="rpipico",
platform="https://github.com/maxgerhardt/platform-raspberrypi.git",
platform_needs_install=True, # Install platform package to get the boards
platform_packages="framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git",
framework="arduino",
board_build_core="earlephilhower",
board_build_filesystem_size="0.5m",
)
RPI_PICO2 = Board(
board_name="rpipico2",
real_board_name="rpipico", # Use the existing Pico board definition until PlatformIO adds native Pico 2 support
platform="https://github.com/maxgerhardt/platform-raspberrypi.git",
platform_needs_install=True, # Install platform package to get the boards
platform_packages="framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git",
framework="arduino",
board_build_core="earlephilhower",
board_build_filesystem_size="0.5m",
)
BLUEPILL = Board(
board_name="bluepill",
real_board_name="bluepill_f103c8",
platform="ststm32",
)
# maple_mini_b20
MAPLE_MINI = Board(
board_name="maple_mini",
real_board_name="maple_mini_b20",
platform="ststm32",
)
HY_TINYSTM103TB = Board(
board_name="hy_tinystm103tb",
platform="ststm32",
)
ATTINY88 = Board(
board_name="attiny88",
platform="atmelavr",
)
# ATtiny1604
ATTINY1604 = Board(
board_name="ATtiny1604",
platform="atmelmegaavr",
)
# attiny4313
ATTINY4313 = Board(
board_name="attiny4313",
platform="atmelavr",
)
ATTINY1616 = Board(
board_name="ATtiny1616",
platform="atmelmegaavr",
)
UNO_R4_WIFI = Board(
board_name="uno_r4_wifi",
platform="renesas-ra",
# I2S library auto-excluded due to missing r_i2s_api.h header in Arduino Renesas framework
)
NANO_EVERY = Board(
board_name="nano_every",
platform="atmelmegaavr",
)
ESP32DEV_I2S = Board(
board_name="esp32dev_i2s",
real_board_name="esp32dev",
platform=ESP32_IDF_4_4_LATEST,
)
ESP32S3_RMT51 = Board(
board_name="esp32rmt_51",
real_board_name="esp32-s3-devkitc-1",
platform_needs_install=True,
platform=ESP32_IDF_5_3_PIOARDUINO,
defines=[
"FASTLED_RMT5=1",
],
)
# Teensy boards
TEENSY_LC = Board(
board_name="teensylc",
platform="teensy",
framework="arduino",
)
TEENSY30 = Board(
board_name="teensy30",
platform="teensy",
framework="arduino",
)
TEENSY31 = Board(
board_name="teensy31",
platform="teensy",
framework="arduino",
)
TEENSY40 = Board(
board_name="teensy40",
platform="teensy",
framework="arduino",
)
TEENSY41 = Board(
board_name="teensy41",
platform="teensy",
framework="arduino",
)
# Basic Arduino boards
UNO = Board(
board_name="uno",
platform="atmelavr",
framework="arduino",
)
YUN = Board(
board_name="yun",
platform="atmelavr",
framework="arduino",
)
DIGIX = Board(
board_name="digix",
real_board_name="due", # Digix is Arduino Due compatible
platform="atmelsam",
framework="arduino",
)
# ESP8266 boards
ESP01 = Board(
board_name="esp01",
platform="espressif8266",
framework="arduino",
)
# ATtiny boards
ATTINY85 = Board(
board_name="attiny85",
platform="atmelavr",
framework="arduino",
)
# Seeed XIAO ESP32S3 board same platform, needs FASTLED_RMT5 macro removal
XIAO_ESP32S3 = Board(
board_name="seeed_xiao_esp32s3",
real_board_name="seeed_xiao_esp32s3",
platform=ESP32_IDF_5_4_PIOARDUINO,
board_partitions="huge_app.csv",
defines=None,
build_unflags=["-DFASTLED_RMT5=0", "-DFASTLED_RMT5"],
)
# STM32F4 Black Pill board - addresses GitHub issue #726
BLACKPILL = Board(
board_name="blackpill",
real_board_name="blackpill_f411ce",
platform="ststm32",
)
# Silicon Labs MGM240S boards (Arduino Nano Matter, SparkFun Thing Plus Matter)
# Uses Silicon Labs EFM32 platform with Arduino framework support
# Based on EFR32MG24 SoC with ARM Cortex-M33 @ 78MHz
MGM240S = Board(
board_name="mgm240",
real_board_name="sparkfun_thingplusmatter",
platform="https://github.com/maxgerhardt/platform-siliconlabsefm32/archive/refs/heads/silabs-arduino.zip",
platform_needs_install=True,
framework="arduino",
)
def _make_board_map(boards: list[Board]) -> dict[str, Board]:
# make board map, but assert on duplicate board names
board_map: dict[str, Board] = {}
for board in boards:
assert board.board_name not in board_map, (
f"Duplicate board name: {board.board_name}"
)
board_map[board.board_name] = board
return board_map
_BOARD_MAP: dict[str, Board] = _make_board_map(ALL)
def create_board(board_name: str, no_project_options: bool = False) -> Board:
board: Board
if no_project_options:
board = Board(board_name=board_name, add_board_to_all=False)
if board_name not in _BOARD_MAP:
# empty board without any special overrides, assume platformio will know what to do with it.
board = Board(board_name=board_name, add_board_to_all=False)
else:
board = _BOARD_MAP[board_name]
return board.clone()