mirror of
https://github.com/esphome/esphome.git
synced 2026-05-28 13:37:24 +08:00
[rp2040] Fix Pico W LED pin and auto-generate board definitions for arduino-pico 5.5.x (#14528)
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
# Auto-generated by generate_boards.py — do not edit manually
|
||||||
|
# To regenerate: python esphome/components/rp2040/generate_boards.py <arduino-pico-path>
|
||||||
|
|
||||||
|
# arduino-pico maps pins >= {{ cyw43_gpio_offset }} to CYW43 wireless chip GPIOs
|
||||||
|
CYW43_GPIO_OFFSET = {{ cyw43_gpio_offset }}
|
||||||
|
CYW43_MAX_GPIO = {{ cyw43_max_gpio }}
|
||||||
|
DEFAULT_MAX_PIN = {{ default_max_pin }}
|
||||||
|
|
||||||
|
RP2040_BASE_PINS = {}
|
||||||
|
|
||||||
|
RP2040_BOARD_PINS = {
|
||||||
|
{%- for name, pins in board_pins %}
|
||||||
|
{{ name | repr }}: {{ pins | format_pins }},
|
||||||
|
{%- endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
BOARDS = {
|
||||||
|
{%- for name, info in boards %}
|
||||||
|
{{ name | repr }}: {
|
||||||
|
{%- for key, value in info.items() %}
|
||||||
|
{{ key | repr }}: {{ value | repr }},
|
||||||
|
{%- endfor %}
|
||||||
|
},
|
||||||
|
{%- endfor %}
|
||||||
|
}
|
||||||
+2188
-11
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
|||||||
|
"""Generate boards.py from arduino-pico board definitions.
|
||||||
|
|
||||||
|
Usage: python esphome/components/rp2040/generate_boards.py <arduino-pico-path>
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
# Map arduino-pico pin defines to ESPHome-friendly names
|
||||||
|
PIN_NAME_MAP = {
|
||||||
|
"LED": "LED",
|
||||||
|
"WIRE0_SDA": "SDA",
|
||||||
|
"WIRE0_SCL": "SCL",
|
||||||
|
"WIRE1_SDA": "SDA1",
|
||||||
|
"WIRE1_SCL": "SCL1",
|
||||||
|
"SPI0_MISO": "MISO",
|
||||||
|
"SPI0_MOSI": "MOSI",
|
||||||
|
"SPI0_SCK": "SCK",
|
||||||
|
"SPI0_SS": "SS",
|
||||||
|
"SERIAL1_TX": "TX",
|
||||||
|
"SERIAL1_RX": "RX",
|
||||||
|
}
|
||||||
|
|
||||||
|
# arduino-pico maps pins >= 64 to CYW43 wireless chip GPIOs (pin - 64)
|
||||||
|
CYW43_GPIO_OFFSET = 64
|
||||||
|
# CYW43 has 3 GPIOs: 0=LED, 1=VBUS_SENSE, 2=REG_ON
|
||||||
|
CYW43_GPIO_COUNT = 3
|
||||||
|
|
||||||
|
# Max GPIO pin per MCU (hardware specs from datasheets)
|
||||||
|
MCU_MAX_PIN = {
|
||||||
|
"rp2040": 29, # GPIO 0-29
|
||||||
|
"rp2350": 47, # GPIO 0-47 (RP2350A)
|
||||||
|
}
|
||||||
|
DEFAULT_MAX_PIN = 29
|
||||||
|
|
||||||
|
PIN_DEFINE_RE = re.compile(r"#define\s+PIN_(\w+)\s+\((\d+)u\)")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_variant_pins(variant_dir: Path) -> dict[str, int]:
|
||||||
|
"""Parse pins_arduino.h and return mapped pin names."""
|
||||||
|
header = variant_dir / "pins_arduino.h"
|
||||||
|
if not header.exists():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
pins = {}
|
||||||
|
for match in PIN_DEFINE_RE.finditer(header.read_text(encoding="utf-8")):
|
||||||
|
raw_name = match.group(1)
|
||||||
|
value = int(match.group(2))
|
||||||
|
if raw_name in PIN_NAME_MAP:
|
||||||
|
pins[PIN_NAME_MAP[raw_name]] = value
|
||||||
|
return pins
|
||||||
|
|
||||||
|
|
||||||
|
def load_boards(arduino_pico_path: Path) -> tuple[dict, dict]:
|
||||||
|
"""Load all board definitions and return (board_pins, boards) dicts."""
|
||||||
|
json_dir = arduino_pico_path / "tools" / "json"
|
||||||
|
variants_dir = arduino_pico_path / "variants"
|
||||||
|
|
||||||
|
board_pins = {}
|
||||||
|
boards = {}
|
||||||
|
variant_pins_cache: dict[str, dict[str, int]] = {}
|
||||||
|
|
||||||
|
for json_file in sorted(json_dir.glob("*.json")):
|
||||||
|
board_name = json_file.stem
|
||||||
|
with open(json_file, encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
build = data.get("build", {})
|
||||||
|
mcu = build.get("mcu", "rp2040")
|
||||||
|
variant = build.get("variant", board_name)
|
||||||
|
name = data.get("name", board_name)
|
||||||
|
vendor = data.get("vendor", "")
|
||||||
|
|
||||||
|
display_name = f"{vendor} {name}".strip() if vendor else name
|
||||||
|
|
||||||
|
boards[board_name] = {
|
||||||
|
"name": display_name,
|
||||||
|
"mcu": mcu,
|
||||||
|
"max_pin": MCU_MAX_PIN.get(mcu, DEFAULT_MAX_PIN),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get pins for this variant
|
||||||
|
if variant not in variant_pins_cache:
|
||||||
|
variant_dir = variants_dir / variant
|
||||||
|
variant_pins_cache[variant] = parse_variant_pins(variant_dir)
|
||||||
|
|
||||||
|
pins = variant_pins_cache[variant]
|
||||||
|
if pins:
|
||||||
|
max_pin = boards[board_name]["max_pin"]
|
||||||
|
cyw43_max = CYW43_GPIO_OFFSET + CYW43_GPIO_COUNT - 1
|
||||||
|
# Filter out placeholder values (e.g. 99 = "not connected")
|
||||||
|
filtered = {
|
||||||
|
name: value
|
||||||
|
for name, value in pins.items()
|
||||||
|
if value <= max_pin or CYW43_GPIO_OFFSET <= value <= cyw43_max
|
||||||
|
}
|
||||||
|
if filtered:
|
||||||
|
board_pins[board_name] = filtered
|
||||||
|
|
||||||
|
# Compute max_virtual_pin per board from pin maps
|
||||||
|
for board_name, pins in board_pins.items():
|
||||||
|
if isinstance(pins, str):
|
||||||
|
continue
|
||||||
|
virtual_pins = [v for v in pins.values() if v >= CYW43_GPIO_OFFSET]
|
||||||
|
if virtual_pins and board_name in boards:
|
||||||
|
boards[board_name]["max_virtual_pin"] = max(virtual_pins)
|
||||||
|
|
||||||
|
# Deduplicate: if board pins match its variant's pins, use string alias
|
||||||
|
for board_name in list(board_pins.keys()):
|
||||||
|
if board_name not in boards:
|
||||||
|
continue
|
||||||
|
build_variant = _get_variant(json_dir / f"{board_name}.json")
|
||||||
|
if (
|
||||||
|
build_variant
|
||||||
|
and build_variant != board_name
|
||||||
|
and build_variant in board_pins
|
||||||
|
and board_pins[board_name] == board_pins[build_variant]
|
||||||
|
):
|
||||||
|
board_pins[board_name] = build_variant
|
||||||
|
|
||||||
|
return board_pins, boards
|
||||||
|
|
||||||
|
|
||||||
|
def _get_variant(json_file: Path) -> str | None:
|
||||||
|
"""Get variant name from a board JSON file."""
|
||||||
|
if not json_file.exists():
|
||||||
|
return None
|
||||||
|
with open(json_file, encoding="utf-8") as f:
|
||||||
|
data = json.load(f)
|
||||||
|
return data.get("build", {}).get("variant")
|
||||||
|
|
||||||
|
|
||||||
|
_TEMPLATE_DIR = Path(__file__).parent
|
||||||
|
|
||||||
|
|
||||||
|
def _format_pins(pins: dict[str, int] | str) -> str:
|
||||||
|
"""Jinja2 filter to format a pin dict or alias as Python source."""
|
||||||
|
if isinstance(pins, str):
|
||||||
|
return repr(pins)
|
||||||
|
items = ", ".join(f"{k!r}: {v}" for k, v in sorted(pins.items()))
|
||||||
|
return f"{{{items}}}"
|
||||||
|
|
||||||
|
|
||||||
|
_jinja_env = Environment(
|
||||||
|
loader=FileSystemLoader(_TEMPLATE_DIR), keep_trailing_newline=True
|
||||||
|
)
|
||||||
|
_jinja_env.filters["format_pins"] = _format_pins
|
||||||
|
_jinja_env.filters["repr"] = repr
|
||||||
|
|
||||||
|
|
||||||
|
def generate(arduino_pico_path: Path) -> str:
|
||||||
|
"""Generate boards.py content."""
|
||||||
|
board_pins, boards = load_boards(arduino_pico_path)
|
||||||
|
|
||||||
|
template = _jinja_env.get_template("boards.jinja2")
|
||||||
|
return template.render(
|
||||||
|
cyw43_gpio_offset=CYW43_GPIO_OFFSET,
|
||||||
|
cyw43_max_gpio=CYW43_GPIO_OFFSET + CYW43_GPIO_COUNT - 1,
|
||||||
|
default_max_pin=DEFAULT_MAX_PIN,
|
||||||
|
board_pins=sorted(board_pins.items()),
|
||||||
|
boards=sorted(boards.items()),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print(f"Usage: {sys.argv[0]} <arduino-pico-path>", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
arduino_pico_path = Path(sys.argv[1])
|
||||||
|
if not (arduino_pico_path / "tools" / "json").exists():
|
||||||
|
print(f"Error: {arduino_pico_path}/tools/json not found", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
output = generate(arduino_pico_path)
|
||||||
|
output_file = Path(__file__).parent / "boards.py"
|
||||||
|
output_file.write_text(output, encoding="utf-8")
|
||||||
|
print(f"Generated {output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -54,19 +54,29 @@ def _translate_pin(value):
|
|||||||
return _lookup_pin(value)
|
return _lookup_pin(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _board_max_virtual_pin(board):
|
||||||
|
"""Get the max CYW43 virtual pin for this board, or None if no virtual pins."""
|
||||||
|
return boards.BOARDS.get(board, {}).get("max_virtual_pin")
|
||||||
|
|
||||||
|
|
||||||
def validate_gpio_pin(value):
|
def validate_gpio_pin(value):
|
||||||
value = _translate_pin(value)
|
value = _translate_pin(value)
|
||||||
board = CORE.data[KEY_RP2040][KEY_BOARD]
|
board = CORE.data[KEY_RP2040][KEY_BOARD]
|
||||||
if board == "rpipicow" and value == 32:
|
max_virtual = _board_max_virtual_pin(board)
|
||||||
return value # Special case for Pico-w LED pin
|
if max_virtual is not None and boards.CYW43_GPIO_OFFSET <= value <= max_virtual:
|
||||||
if value < 0 or value > 29:
|
return value
|
||||||
raise cv.Invalid(f"RP2040: Invalid pin number: {value}")
|
max_pin = boards.BOARDS.get(board, {}).get("max_pin", boards.DEFAULT_MAX_PIN)
|
||||||
|
if value < 0 or value > max_pin:
|
||||||
|
raise cv.Invalid(f"Invalid pin number: {value} (max {max_pin} for this board)")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def validate_supports(value):
|
def validate_supports(value):
|
||||||
board = CORE.data[KEY_RP2040][KEY_BOARD]
|
board = CORE.data[KEY_RP2040][KEY_BOARD]
|
||||||
if board != "rpipicow" or value[CONF_NUMBER] != 32:
|
if (
|
||||||
|
_board_max_virtual_pin(board) is None
|
||||||
|
or value[CONF_NUMBER] < boards.CYW43_GPIO_OFFSET
|
||||||
|
):
|
||||||
return value
|
return value
|
||||||
mode = value[CONF_MODE]
|
mode = value[CONF_MODE]
|
||||||
is_input = mode[CONF_INPUT]
|
is_input = mode[CONF_INPUT]
|
||||||
@@ -75,7 +85,7 @@ def validate_supports(value):
|
|||||||
is_pullup = mode[CONF_PULLUP]
|
is_pullup = mode[CONF_PULLUP]
|
||||||
is_pulldown = mode[CONF_PULLDOWN]
|
is_pulldown = mode[CONF_PULLDOWN]
|
||||||
if not is_output or is_input or is_open_drain or is_pullup or is_pulldown:
|
if not is_output or is_input or is_open_drain or is_pullup or is_pulldown:
|
||||||
raise cv.Invalid("Only output mode is supported for Pico-w LED pin")
|
raise cv.Invalid("Only output mode is supported for CYW43 virtual pins")
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,273 @@
|
|||||||
|
"""Tests for rp2040 generate_boards.py."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import textwrap
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from esphome.components.rp2040.generate_boards import load_boards, parse_variant_pins
|
||||||
|
|
||||||
|
PICO_PINS_HEADER = textwrap.dedent("""\
|
||||||
|
#pragma once
|
||||||
|
#define PIN_LED (25u)
|
||||||
|
#define PIN_SERIAL1_TX (0u)
|
||||||
|
#define PIN_SERIAL1_RX (1u)
|
||||||
|
#define PIN_WIRE0_SDA (4u)
|
||||||
|
#define PIN_WIRE0_SCL (5u)
|
||||||
|
#define PIN_WIRE1_SDA (26u)
|
||||||
|
#define PIN_WIRE1_SCL (27u)
|
||||||
|
#define PIN_SPI0_MISO (16u)
|
||||||
|
#define PIN_SPI0_MOSI (19u)
|
||||||
|
#define PIN_SPI0_SCK (18u)
|
||||||
|
#define PIN_SPI0_SS (17u)
|
||||||
|
#include "../generic/common.h"
|
||||||
|
""")
|
||||||
|
|
||||||
|
PICOW_PINS_HEADER = textwrap.dedent("""\
|
||||||
|
#pragma once
|
||||||
|
#include <cyw43_wrappers.h>
|
||||||
|
#define PIN_LED (64u)
|
||||||
|
#define PIN_WIRE0_SDA (4u)
|
||||||
|
#define PIN_WIRE0_SCL (5u)
|
||||||
|
#include "../generic/common.h"
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def arduino_pico(tmp_path: Path) -> Path:
|
||||||
|
"""Create a minimal arduino-pico directory structure."""
|
||||||
|
json_dir = tmp_path / "tools" / "json"
|
||||||
|
json_dir.mkdir(parents=True)
|
||||||
|
variants_dir = tmp_path / "variants"
|
||||||
|
variants_dir.mkdir()
|
||||||
|
|
||||||
|
generic_dir = variants_dir / "generic"
|
||||||
|
generic_dir.mkdir()
|
||||||
|
(generic_dir / "common.h").write_text("#pragma once\n")
|
||||||
|
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
def _add_board(
|
||||||
|
arduino_pico: Path,
|
||||||
|
board_name: str,
|
||||||
|
mcu: str = "rp2040",
|
||||||
|
variant: str | None = None,
|
||||||
|
vendor: str = "",
|
||||||
|
name: str | None = None,
|
||||||
|
pins_header: str | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Add a board JSON and variant to the fake arduino-pico tree."""
|
||||||
|
if variant is None:
|
||||||
|
variant = board_name
|
||||||
|
if name is None:
|
||||||
|
name = board_name
|
||||||
|
|
||||||
|
json_dir = arduino_pico / "tools" / "json"
|
||||||
|
variants_dir = arduino_pico / "variants"
|
||||||
|
|
||||||
|
board_json = {
|
||||||
|
"build": {
|
||||||
|
"mcu": mcu,
|
||||||
|
"variant": variant,
|
||||||
|
},
|
||||||
|
"name": name,
|
||||||
|
"vendor": vendor,
|
||||||
|
}
|
||||||
|
(json_dir / f"{board_name}.json").write_text(json.dumps(board_json))
|
||||||
|
|
||||||
|
variant_dir = variants_dir / variant
|
||||||
|
variant_dir.mkdir(exist_ok=True)
|
||||||
|
if pins_header is not None:
|
||||||
|
(variant_dir / "pins_arduino.h").write_text(pins_header)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_basic_pins(tmp_path: Path) -> None:
|
||||||
|
variant_dir = tmp_path / "rpipico"
|
||||||
|
variant_dir.mkdir()
|
||||||
|
(variant_dir / "pins_arduino.h").write_text(PICO_PINS_HEADER)
|
||||||
|
|
||||||
|
pins = parse_variant_pins(variant_dir)
|
||||||
|
assert pins["LED"] == 25
|
||||||
|
assert pins["SDA"] == 4
|
||||||
|
assert pins["SCL"] == 5
|
||||||
|
assert pins["SDA1"] == 26
|
||||||
|
assert pins["SCL1"] == 27
|
||||||
|
assert pins["MISO"] == 16
|
||||||
|
assert pins["MOSI"] == 19
|
||||||
|
assert pins["SCK"] == 18
|
||||||
|
assert pins["SS"] == 17
|
||||||
|
assert pins["TX"] == 0
|
||||||
|
assert pins["RX"] == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_cyw43_led_pin(tmp_path: Path) -> None:
|
||||||
|
variant_dir = tmp_path / "rpipicow"
|
||||||
|
variant_dir.mkdir()
|
||||||
|
(variant_dir / "pins_arduino.h").write_text(PICOW_PINS_HEADER)
|
||||||
|
|
||||||
|
pins = parse_variant_pins(variant_dir)
|
||||||
|
assert pins["LED"] == 64
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_missing_header(tmp_path: Path) -> None:
|
||||||
|
variant_dir = tmp_path / "noheader"
|
||||||
|
variant_dir.mkdir()
|
||||||
|
assert parse_variant_pins(variant_dir) == {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_unmapped_defines_ignored(tmp_path: Path) -> None:
|
||||||
|
variant_dir = tmp_path / "custom"
|
||||||
|
variant_dir.mkdir()
|
||||||
|
(variant_dir / "pins_arduino.h").write_text(
|
||||||
|
"#define PIN_NEOPIXEL (16u)\n#define PIN_LED (25u)\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
pins = parse_variant_pins(variant_dir)
|
||||||
|
assert "NEOPIXEL" not in pins
|
||||||
|
assert pins["LED"] == 25
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_basic_board(arduino_pico: Path) -> None:
|
||||||
|
_add_board(
|
||||||
|
arduino_pico,
|
||||||
|
"rpipico",
|
||||||
|
vendor="Raspberry Pi",
|
||||||
|
name="Pico",
|
||||||
|
pins_header=PICO_PINS_HEADER,
|
||||||
|
)
|
||||||
|
|
||||||
|
board_pins, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert "rpipico" in boards
|
||||||
|
assert boards["rpipico"]["name"] == "Raspberry Pi Pico"
|
||||||
|
assert boards["rpipico"]["mcu"] == "rp2040"
|
||||||
|
assert boards["rpipico"]["max_pin"] == 29
|
||||||
|
|
||||||
|
assert "rpipico" in board_pins
|
||||||
|
assert board_pins["rpipico"]["LED"] == 25
|
||||||
|
assert board_pins["rpipico"]["SDA"] == 4
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_rp2350_board(arduino_pico: Path) -> None:
|
||||||
|
_add_board(
|
||||||
|
arduino_pico,
|
||||||
|
"rpipico2",
|
||||||
|
mcu="rp2350",
|
||||||
|
vendor="Raspberry Pi",
|
||||||
|
name="Pico 2",
|
||||||
|
pins_header=PICO_PINS_HEADER,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert boards["rpipico2"]["mcu"] == "rp2350"
|
||||||
|
assert boards["rpipico2"]["max_pin"] == 47
|
||||||
|
|
||||||
|
|
||||||
|
def test_cyw43_board_has_max_virtual_pin(arduino_pico: Path) -> None:
|
||||||
|
_add_board(
|
||||||
|
arduino_pico,
|
||||||
|
"rpipicow",
|
||||||
|
vendor="Raspberry Pi",
|
||||||
|
name="Pico W",
|
||||||
|
pins_header=PICOW_PINS_HEADER,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert boards["rpipicow"]["max_virtual_pin"] == 64
|
||||||
|
|
||||||
|
|
||||||
|
def test_non_cyw43_board_has_no_max_virtual_pin(arduino_pico: Path) -> None:
|
||||||
|
_add_board(
|
||||||
|
arduino_pico,
|
||||||
|
"rpipico",
|
||||||
|
vendor="Raspberry Pi",
|
||||||
|
name="Pico",
|
||||||
|
pins_header=PICO_PINS_HEADER,
|
||||||
|
)
|
||||||
|
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert "max_virtual_pin" not in boards["rpipico"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_board_without_variant_header(arduino_pico: Path) -> None:
|
||||||
|
_add_board(arduino_pico, "novariant", name="No Variant")
|
||||||
|
|
||||||
|
board_pins, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert "novariant" in boards
|
||||||
|
assert "novariant" not in board_pins
|
||||||
|
|
||||||
|
|
||||||
|
def test_shared_variant_deduplicates(arduino_pico: Path) -> None:
|
||||||
|
"""Two boards sharing the same variant should alias."""
|
||||||
|
_add_board(arduino_pico, "base_board", pins_header=PICO_PINS_HEADER)
|
||||||
|
_add_board(arduino_pico, "alias_board", variant="base_board")
|
||||||
|
|
||||||
|
board_pins, _ = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert board_pins["base_board"] == parse_variant_pins(
|
||||||
|
arduino_pico / "variants" / "base_board"
|
||||||
|
)
|
||||||
|
assert board_pins["alias_board"] == "base_board"
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_name_with_vendor(arduino_pico: Path) -> None:
|
||||||
|
_add_board(arduino_pico, "testboard", vendor="Acme", name="Widget")
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
assert boards["testboard"]["name"] == "Acme Widget"
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_name_without_vendor(arduino_pico: Path) -> None:
|
||||||
|
_add_board(arduino_pico, "testboard", vendor="", name="Widget")
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
assert boards["testboard"]["name"] == "Widget"
|
||||||
|
|
||||||
|
|
||||||
|
def test_unknown_mcu_gets_default_max_pin(arduino_pico: Path) -> None:
|
||||||
|
_add_board(arduino_pico, "future", mcu="rp2450", pins_header=PICO_PINS_HEADER)
|
||||||
|
_, boards = load_boards(arduino_pico)
|
||||||
|
assert boards["future"]["max_pin"] == 29
|
||||||
|
|
||||||
|
|
||||||
|
def test_placeholder_pins_filtered_out(arduino_pico: Path) -> None:
|
||||||
|
"""Pins with placeholder values like 99 should be filtered out."""
|
||||||
|
header = textwrap.dedent("""\
|
||||||
|
#pragma once
|
||||||
|
#define PIN_LED (25u)
|
||||||
|
#define PIN_WIRE0_SDA (4u)
|
||||||
|
#define PIN_WIRE0_SCL (5u)
|
||||||
|
#define PIN_WIRE1_SDA (99u)
|
||||||
|
#define PIN_WIRE1_SCL (99u)
|
||||||
|
""")
|
||||||
|
_add_board(arduino_pico, "placeholder", pins_header=header)
|
||||||
|
|
||||||
|
board_pins, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert "SDA1" not in board_pins["placeholder"]
|
||||||
|
assert "SCL1" not in board_pins["placeholder"]
|
||||||
|
assert board_pins["placeholder"]["LED"] == 25
|
||||||
|
assert "max_virtual_pin" not in boards["placeholder"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_placeholder_pins_not_treated_as_virtual(arduino_pico: Path) -> None:
|
||||||
|
"""Pin 99 should not cause max_virtual_pin to be set."""
|
||||||
|
header = textwrap.dedent("""\
|
||||||
|
#pragma once
|
||||||
|
#define PIN_LED (64u)
|
||||||
|
#define PIN_WIRE0_SDA (4u)
|
||||||
|
#define PIN_WIRE0_SCL (5u)
|
||||||
|
#define PIN_SPI0_MISO (99u)
|
||||||
|
""")
|
||||||
|
_add_board(arduino_pico, "badpin", pins_header=header)
|
||||||
|
|
||||||
|
board_pins, boards = load_boards(arduino_pico)
|
||||||
|
|
||||||
|
assert "MISO" not in board_pins["badpin"]
|
||||||
|
assert boards["badpin"]["max_virtual_pin"] == 64
|
||||||
Reference in New Issue
Block a user