initial commit

This commit is contained in:
2026-02-12 00:45:31 -08:00
commit 5f168f370b
3024 changed files with 804889 additions and 0 deletions

View File

@@ -0,0 +1,769 @@
# 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()