From 8007a1e819de73abe0a6ed259714f07261adabe2 Mon Sep 17 00:00:00 2001 From: Arnav Bajaj <49639514+Jackhammer9@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:29:05 +0100 Subject: [PATCH 1/4] Add platform and board validation to target workflow --- README.md | 39 +++++++++++++++++--- src/Reduino/__init__.py | 38 +++++++++++++++++--- src/Reduino/toolchain/pio.py | 64 ++++++++++++++++++++++++++++++--- src/Reduino/transpile/parser.py | 6 ++-- tests/test_toolchain.py | 38 +++++++++++++++++++- 5 files changed, 170 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 3181771..5a6f456 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,12 @@ pip install platformio # required for automatic uploads Place `target()` **at the very top of your script**, immediately after imports. This is the entry point that tells Reduino to parse your entire file, transpile it to Arduino C++, and (optionally) upload it. -| Parameter | Type | Default | Description | -| --------: | :----: | :-----: | ----------------------------------------------------------------------- | -| `port` | `str` | — | Serial port, e.g. `"COM3"` or `"/dev/ttyACM0"`. | -| `upload` | `bool` | `True` | If `True`, compile & upload via PlatformIO. If `False`, only transpile. | +| Parameter | Type | Default | Description | +| ---------: | :----: | :----------: | --------------------------------------------------------------------------- | +| `port` | `str` | — | Serial port, e.g. `"COM3"` or `"/dev/ttyACM0"`. | +| `upload` | `bool` | `True` | If `True`, compile & upload via PlatformIO. If `False`, only transpile. | +| `platform` | `str` | `"atmelavr"` | PlatformIO platform ID, e.g. `"espressif32"` for ESP32 boards. | +| `board` | `str` | `"uno"` | PlatformIO board ID, e.g. `"esp32dev"`. Must be compatible with `platform`. | **Returns:** `str` of the generated Arduino C++ source. @@ -145,6 +147,35 @@ print(cpp) # Your Reduino code below... ``` +### Targeting different platforms & boards + +Reduino validates that the requested PlatformIO platform/board pair is supported. The +following combinations are available out of the box: + +| PlatformIO platform | Supported boards | +| ------------------- | -------------------------------- | +| `atmelavr` | `uno`, `nano` | +| `atmelsam` | `due` | +| `espressif32` | `esp32dev`, `esp32doit-devkit-v1` | + +If you pick an unsupported board, or a board that does not belong to the selected +platform, `target()` raises a `ValueError` with a helpful message. + +```python +from Reduino import target + +# Build for an ESP32 dev module without uploading automatically. +target( + "COM9", + upload=False, + platform="espressif32", + board="esp32dev", +) + +# Build for an Arduino Nano and upload immediately. +target("/dev/ttyUSB0", platform="atmelavr", board="nano") +``` + > [!IMPORTANT] > `target()` reads the whole file text and generates code for everything below it. > If `upload=True`, it also builds and flashes using a temporary PlatformIO project. diff --git a/src/Reduino/__init__.py b/src/Reduino/__init__.py index 3a5c325..af2ee94 100644 --- a/src/Reduino/__init__.py +++ b/src/Reduino/__init__.py @@ -10,7 +10,12 @@ import tempfile from typing import List, Type -from Reduino.toolchain.pio import compile_upload, ensure_pio, write_project +from Reduino.toolchain.pio import ( + compile_upload, + ensure_pio, + validate_platform_board, + write_project, +) from Reduino.transpile.ast import LCDDecl, Program, ServoDecl from Reduino.transpile.emitter import emit from Reduino.transpile.parser import parse @@ -83,7 +88,13 @@ def _visit(value: object) -> None: return requirements -def target(port: str, *, upload: bool = True) -> None: +def target( + port: str, + *, + upload: bool = True, + platform: str = "atmelavr", + board: str = "uno", +) -> None: """Transpile the invoking script and prepare a PlatformIO project. Parameters @@ -95,8 +106,20 @@ def target(port: str, *, upload: bool = True) -> None: after generating the temporary project directory. Uploading is disabled by default so that unit tests can exercise the helper without requiring an Arduino board to be connected. + platform: + PlatformIO platform identifier. Defaults to ``"atmelavr"`` for Arduino + AVR boards. + board: + PlatformIO board identifier. Defaults to ``"uno"``. + + Raises + ------ + ValueError + If the requested platform or board is not supported or if the + combination is incompatible. """ + validate_platform_board(platform, board) ensure_pio() main_file = pathlib.Path(sys.modules["__main__"].__file__) @@ -112,8 +135,15 @@ def target(port: str, *, upload: bool = True) -> None: cpp = emit(program) tmp = pathlib.Path(tempfile.mkdtemp(prefix="reduino-pio-")) - write_project(tmp, cpp, port=port, lib_deps=required_libs) + write_project( + tmp, + cpp, + port=port, + platform=platform, + board=board, + lib_deps=required_libs, + ) if upload: compile_upload(tmp) - return cpp \ No newline at end of file + return cpp diff --git a/src/Reduino/toolchain/pio.py b/src/Reduino/toolchain/pio.py index 08d029a..46bf48b 100644 --- a/src/Reduino/toolchain/pio.py +++ b/src/Reduino/toolchain/pio.py @@ -1,12 +1,26 @@ from __future__ import annotations +import re import subprocess from pathlib import Path from typing import Iterable, List -PIO_INI = """[env:uno] -platform = atmelavr -board = uno +SUPPORTED_PLATFORMS: dict[str, set[str]] = { + "atmelavr": {"uno", "nano"}, + "atmelsam": {"due"}, + "espressif32": {"esp32dev", "esp32doit-devkit-v1"}, +} + +BOARD_TO_PLATFORM = { + board: platform + for platform, boards in SUPPORTED_PLATFORMS.items() + for board in boards +} + + +PIO_INI = """[env:{env_name}] +platform = {platform} +board = {board} framework = arduino upload_port = {port} @@ -34,6 +48,35 @@ def _format_lib_section(libraries: Iterable[str] | None) -> str: lines.extend(f" {name}" for name in unique) return "\n".join(lines) + +def _sanitize_env_name(board: str) -> str: + """Return a safe PlatformIO environment name based on ``board``.""" + + return re.sub(r"[^A-Za-z0-9_]+", "_", board) + + +def validate_platform_board(platform: str, board: str) -> None: + """Ensure the requested PlatformIO ``platform``/``board`` pair is supported.""" + + if platform not in SUPPORTED_PLATFORMS: + supported = ", ".join(sorted(SUPPORTED_PLATFORMS)) + raise ValueError( + f"Unsupported PlatformIO platform '{platform}'. Supported platforms: {supported}." + ) + + if board not in BOARD_TO_PLATFORM: + supported = ", ".join(sorted(BOARD_TO_PLATFORM)) + raise ValueError( + f"Unsupported PlatformIO board '{board}'. Supported boards: {supported}." + ) + + required_platform = BOARD_TO_PLATFORM[board] + if required_platform != platform: + raise ValueError( + f"Board '{board}' requires PlatformIO platform '{required_platform}', not '{platform}'." + ) + + def ensure_pio() -> None: try: subprocess.run(["pio", "--version"], check=True, stdout=subprocess.DEVNULL) @@ -47,12 +90,25 @@ def write_project( cpp_code: str, port: str, *, + platform: str = "atmelavr", + board: str = "uno", lib_deps: Iterable[str] | None = None, ) -> None: + validate_platform_board(platform, board) (project_dir / "src").mkdir(parents=True, exist_ok=True) (project_dir / "src" / "main.cpp").write_text(cpp_code, encoding="utf-8") lib_section = _format_lib_section(lib_deps) - ini_contents = PIO_INI.format(port=port, lib_section=lib_section).rstrip() + "\n" + env_name = _sanitize_env_name(board) + ini_contents = ( + PIO_INI.format( + env_name=env_name, + platform=platform, + board=board, + port=port, + lib_section=lib_section, + ).rstrip() + + "\n" + ) (project_dir / "platformio.ini").write_text(ini_contents, encoding="utf-8") def compile_upload(project_dir: str | Path) -> None: diff --git a/src/Reduino/transpile/parser.py b/src/Reduino/transpile/parser.py index 545bd00..888ebb0 100644 --- a/src/Reduino/transpile/parser.py +++ b/src/Reduino/transpile/parser.py @@ -1294,9 +1294,11 @@ def _merge_return_types(types: List[str], has_void: bool) -> str: RE_SLEEP_EXPR = re.compile(r"^\s*sleep\s*\(\s*(.+?)\s*\)\s*$") # Build directive: regex for target device -RE_TARGET_CALL = re.compile(r"""^\s*target\s*\(\s*(?:['"])?\s*([A-Za-z0-9:_\-./\\~]+)\s*(?:['"])?\s*\)\s*$""") +RE_TARGET_CALL = re.compile( + r"""^\s*target\s*\(\s*(?:port\s*=\s*)?(?:['"])?\s*([A-Za-z0-9:_\-./\\~]+)\s*(?:['"])?(?:\s*,[^)]*)?\)\s*$""" +) RE_TARGET_INLINE = re.compile( - r"""(? None: tmp_path, cpp_code="void setup() {}\nvoid loop() {}\n", port="/dev/ttyACM0", + platform="atmelavr", + board="uno", lib_deps=["Servo"], ) ini = _read_ini(tmp_path) @@ -28,11 +33,42 @@ def test_write_project_omits_empty_lib_deps(tmp_path) -> None: tmp_path, cpp_code="void setup() {}\nvoid loop() {}\n", port="/dev/ttyACM0", + platform="atmelavr", + board="uno", ) ini = _read_ini(tmp_path) assert "lib_deps" not in ini +def test_write_project_renders_platform_and_board(tmp_path) -> None: + write_project( + tmp_path, + cpp_code="void setup() {}\nvoid loop() {}\n", + port="/dev/ttyUSB0", + platform="espressif32", + board="esp32dev", + ) + ini = _read_ini(tmp_path) + assert "[env:esp32dev]" in ini + assert "platform = espressif32" in ini + assert "board = esp32dev" in ini + + +def test_validate_platform_board_rejects_unknown_platform() -> None: + with pytest.raises(ValueError): + validate_platform_board("mystery", "uno") + + +def test_validate_platform_board_rejects_unknown_board() -> None: + with pytest.raises(ValueError): + validate_platform_board("atmelavr", "not-a-board") + + +def test_validate_platform_board_rejects_mismatched_pair() -> None: + with pytest.raises(ValueError): + validate_platform_board("espressif32", "uno") + + def test_collect_required_libraries_detects_servo() -> None: program = Program(setup_body=[ServoDecl(name="servo", pin=9)]) assert _collect_required_libraries(program) == ["Servo"] From 81ce513632400f96b4a8cadf82e7af42f8cbed98 Mon Sep 17 00:00:00 2001 From: Arnav Bajaj <49639514+Jackhammer9@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:38:15 +0100 Subject: [PATCH 2/4] Support AVR and megaAVR PlatformIO boards --- README.md | 31 ++-- src/Reduino/toolchain/pio.py | 323 ++++++++++++++++++++++++++++++++++- tests/test_toolchain.py | 12 +- 3 files changed, 339 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 5a6f456..cf87301 100644 --- a/README.md +++ b/README.md @@ -94,8 +94,8 @@ Place `target()` **at the very top of your script**, immediately after imports. | ---------: | :----: | :----------: | --------------------------------------------------------------------------- | | `port` | `str` | — | Serial port, e.g. `"COM3"` or `"/dev/ttyACM0"`. | | `upload` | `bool` | `True` | If `True`, compile & upload via PlatformIO. If `False`, only transpile. | -| `platform` | `str` | `"atmelavr"` | PlatformIO platform ID, e.g. `"espressif32"` for ESP32 boards. | -| `board` | `str` | `"uno"` | PlatformIO board ID, e.g. `"esp32dev"`. Must be compatible with `platform`. | +| `platform` | `str` | `"atmelavr"` | PlatformIO platform ID. Reduino currently supports `atmelavr` and `atmelmegaavr`. | +| `board` | `str` | `"uno"` | PlatformIO board ID. Must be compatible with `platform`. | **Returns:** `str` of the generated Arduino C++ source. @@ -149,31 +149,30 @@ print(cpp) ### Targeting different platforms & boards -Reduino validates that the requested PlatformIO platform/board pair is supported. The -following combinations are available out of the box: +Reduino validates that the requested PlatformIO platform/board pair is supported. +At the moment two PlatformIO platforms are available: -| PlatformIO platform | Supported boards | -| ------------------- | -------------------------------- | -| `atmelavr` | `uno`, `nano` | -| `atmelsam` | `due` | -| `espressif32` | `esp32dev`, `esp32doit-devkit-v1` | +* `atmelavr` – classic AVR-based boards (Uno, Nano, Leonardo, etc.). +* `atmelmegaavr` – newer megaAVR devices (Nano Every, Uno WiFi Rev2, Curiosity Nano kits, ...). -If you pick an unsupported board, or a board that does not belong to the selected -platform, `target()` raises a `ValueError` with a helpful message. +Every board listed in the [PlatformIO board registry for `atmelavr`](https://docs.platformio.org/en/latest/boards/atmelavr.html) +and [`atmelmegaavr`](https://docs.platformio.org/en/latest/boards/atmelmegaavr.html) can be targeted. If you choose an +unsupported board, or one that does not belong to the selected platform, `target()` raises +a `ValueError` with a helpful message. ```python from Reduino import target -# Build for an ESP32 dev module without uploading automatically. +# Build for an Arduino Nano Every without uploading automatically. target( "COM9", upload=False, - platform="espressif32", - board="esp32dev", + platform="atmelmegaavr", + board="nano_every", ) -# Build for an Arduino Nano and upload immediately. -target("/dev/ttyUSB0", platform="atmelavr", board="nano") +# Build for a classic Arduino Uno and upload immediately. +target("/dev/ttyUSB0", platform="atmelavr", board="uno") ``` > [!IMPORTANT] diff --git a/src/Reduino/toolchain/pio.py b/src/Reduino/toolchain/pio.py index 46bf48b..432622b 100644 --- a/src/Reduino/toolchain/pio.py +++ b/src/Reduino/toolchain/pio.py @@ -5,10 +5,322 @@ from pathlib import Path from typing import Iterable, List +SUPPORTED_ATMELAVR_BOARDS = frozenset( + """ + 1284p16m + 1284p8m + 168pa16m + 168pa8m + 328p16m + 328p8m + 32u416m + 644pa16m + 644pa8m + AT90CAN128 + AT90CAN32 + AT90CAN64 + ATmega128 + ATmega1280 + ATmega1281 + ATmega1284 + ATmega1284P + ATmega16 + ATmega162 + ATmega164A + ATmega164P + ATmega165 + ATmega165P + ATmega168 + ATmega168P + ATmega168PB + ATmega169A + ATmega169P + ATmega2560 + ATmega2561 + ATmega32 + ATmega324A + ATmega324P + ATmega324PA + ATmega324PB + ATmega325 + ATmega3250 + ATmega3250P + ATmega325P + ATmega328 + ATmega328P + ATmega328PB + ATmega329 + ATmega3290 + ATmega3290P + ATmega329P + ATmega48 + ATmega48P + ATmega48PB + ATmega64 + ATmega640 + ATmega644A + ATmega644P + ATmega645 + ATmega6450 + ATmega6450P + ATmega645P + ATmega649 + ATmega6490 + ATmega6490P + ATmega649P + ATmega8 + ATmega8515 + ATmega8535 + ATmega88 + ATmega88P + ATmega88PB + LilyPadUSB + a-star32U4 + alorium_hinj + alorium_sno + alorium_xlr8 + altair + ardhat + arduboy + arduboy_devkit + at90pwm216 + at90pwm316 + atmegangatmega168 + atmegangatmega8 + attiny13 + attiny13a + attiny1634 + attiny167 + attiny2313 + attiny24 + attiny25 + attiny261 + attiny43 + attiny4313 + attiny44 + attiny441 + attiny45 + attiny461 + attiny48 + attiny828 + attiny84 + attiny841 + attiny85 + attiny861 + attiny87 + attiny88 + blend + blendmicro16 + blendmicro8 + bluefruitmicro + bob3 + btatmega168 + btatmega328 + chiwawa + circuitplay_classic + controllino_maxi + controllino_maxi_automation + controllino_mega + controllino_mini + diecimilaatmega168 + diecimilaatmega328 + digispark-pro + digispark-pro32 + digispark-pro64 + digispark-tiny + dwenguino + elektor_uno_r4 + emonpi + engduinov3 + esplora + ethernet + feather328p + feather32u4 + fio + flora8 + ftduino + fysetc_f6_13 + gemma + itsybitsy32u4_3V + itsybitsy32u4_5V + leonardo + leonardoeth + lightblue-bean + lightblue-beanplus + lightup + lilypadatmega168 + lilypadatmega328 + lora32u4II + mayfly + megaADK + megaatmega1280 + megaatmega2560 + metro + micro + mightyhat + miniatmega168 + miniatmega328 + miniwireless + moteino + moteino8mhz + moteinomega + nanoatmega168 + nanoatmega328 + nanoatmega328new + nibo2 + nibobee + nibobee_1284 + niboburger + niboburger_1284 + one + panStampAVR + pinoccio + pro16MHzatmega168 + pro16MHzatmega328 + pro8MHzatmega168 + pro8MHzatmega328 + protrinket3 + protrinket3ftdi + protrinket5 + protrinket5ftdi + prusa_mm_control + prusa_rambo + quirkbot + raspduino + reprap_rambo + robotControl + robotMotor + sanguino_atmega1284_8m + sanguino_atmega1284p + sanguino_atmega644 + sanguino_atmega644_8m + sanguino_atmega644p + sanguino_atmega644p_8m + seeeduino + sleepypi + smart7688 + sodaq_galora + sodaq_mbili + sodaq_moja + sodaq_ndogo + sodaq_tatu + sparkfun_digitalsandbox + sparkfun_fiov3 + sparkfun_makeymakey + sparkfun_megamini + sparkfun_megapro16MHz + sparkfun_megapro8MHz + sparkfun_promicro16 + sparkfun_promicro8 + sparkfun_qduinomini + sparkfun_redboard + sparkfun_satmega128rfa1 + sparkfun_serial7seg + the_things_uno + tinyduino + tinylily + trinket3 + trinket5 + uno + uno_mini + usbasp + uview + whispernode + wildfirev2 + wildfirev3 + yun + yunmini + zumbt328 + """.split() +) + +SUPPORTED_ATMELMEGAAVR_BOARDS = frozenset( + """ + ATmega1608 + ATmega1609 + ATmega3208 + ATmega3209 + ATmega4808 + ATmega4809 + ATmega808 + ATmega809 + ATtiny1604 + ATtiny1606 + ATtiny1607 + ATtiny1614 + ATtiny1616 + ATtiny1617 + ATtiny1624 + ATtiny1626 + ATtiny1627 + ATtiny202 + ATtiny204 + ATtiny212 + ATtiny214 + ATtiny3216 + ATtiny3217 + ATtiny3224 + ATtiny3226 + ATtiny3227 + ATtiny402 + ATtiny404 + ATtiny406 + ATtiny412 + ATtiny414 + ATtiny416 + ATtiny417 + ATtiny424 + ATtiny426 + ATtiny427 + ATtiny804 + ATtiny806 + ATtiny807 + ATtiny814 + ATtiny816 + ATtiny817 + ATtiny824 + ATtiny826 + ATtiny827 + AVR128DA28 + AVR128DA32 + AVR128DA48 + AVR128DA64 + AVR128DB28 + AVR128DB32 + AVR128DB48 + AVR128DB64 + AVR32DA28 + AVR32DA32 + AVR32DA48 + AVR32DB28 + AVR32DB32 + AVR32DB48 + AVR64DA28 + AVR64DA32 + AVR64DA48 + AVR64DA64 + AVR64DB28 + AVR64DB32 + AVR64DB48 + AVR64DB64 + AVR64DD14 + AVR64DD20 + AVR64DD28 + AVR64DD32 + avr_iot_wg + curiosity_nano_4809 + curiosity_nano_da + curiosity_nano_db + nano_every + uno_wifi_rev2 + xplained_nano_416 + xplained_pro_4809 + """.split() +) + SUPPORTED_PLATFORMS: dict[str, set[str]] = { - "atmelavr": {"uno", "nano"}, - "atmelsam": {"due"}, - "espressif32": {"esp32dev", "esp32doit-devkit-v1"}, + "atmelavr": SUPPORTED_ATMELAVR_BOARDS, + "atmelmegaavr": SUPPORTED_ATMELMEGAAVR_BOARDS, } BOARD_TO_PLATFORM = { @@ -65,9 +377,10 @@ def validate_platform_board(platform: str, board: str) -> None: ) if board not in BOARD_TO_PLATFORM: - supported = ", ".join(sorted(BOARD_TO_PLATFORM)) raise ValueError( - f"Unsupported PlatformIO board '{board}'. Supported boards: {supported}." + f"Unsupported PlatformIO board '{board}'. Supported boards for the AVR platforms " + "are listed at https://docs.platformio.org/en/latest/boards/atmelavr.html " + "and https://docs.platformio.org/en/latest/boards/atmelmegaavr.html." ) required_platform = BOARD_TO_PLATFORM[board] diff --git a/tests/test_toolchain.py b/tests/test_toolchain.py index 4178974..c0f62fc 100644 --- a/tests/test_toolchain.py +++ b/tests/test_toolchain.py @@ -45,13 +45,13 @@ def test_write_project_renders_platform_and_board(tmp_path) -> None: tmp_path, cpp_code="void setup() {}\nvoid loop() {}\n", port="/dev/ttyUSB0", - platform="espressif32", - board="esp32dev", + platform="atmelmegaavr", + board="nano_every", ) ini = _read_ini(tmp_path) - assert "[env:esp32dev]" in ini - assert "platform = espressif32" in ini - assert "board = esp32dev" in ini + assert "[env:nano_every]" in ini + assert "platform = atmelmegaavr" in ini + assert "board = nano_every" in ini def test_validate_platform_board_rejects_unknown_platform() -> None: @@ -66,7 +66,7 @@ def test_validate_platform_board_rejects_unknown_board() -> None: def test_validate_platform_board_rejects_mismatched_pair() -> None: with pytest.raises(ValueError): - validate_platform_board("espressif32", "uno") + validate_platform_board("atmelavr", "nano_every") def test_collect_required_libraries_detects_servo() -> None: From 570420031a0db1818a126aec2e8e9d13187195f4 Mon Sep 17 00:00:00 2001 From: Arnav Bajaj <49639514+Jackhammer9@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:46:43 +0100 Subject: [PATCH 3/4] Fixed raise value url for supported boards --- src/Reduino/toolchain/pio.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Reduino/toolchain/pio.py b/src/Reduino/toolchain/pio.py index 432622b..f1923c0 100644 --- a/src/Reduino/toolchain/pio.py +++ b/src/Reduino/toolchain/pio.py @@ -379,8 +379,7 @@ def validate_platform_board(platform: str, board: str) -> None: if board not in BOARD_TO_PLATFORM: raise ValueError( f"Unsupported PlatformIO board '{board}'. Supported boards for the AVR platforms " - "are listed at https://docs.platformio.org/en/latest/boards/atmelavr.html " - "and https://docs.platformio.org/en/latest/boards/atmelmegaavr.html." + "are listed at https://docs.platformio.org/en/latest/boards/index.html " ) required_platform = BOARD_TO_PLATFORM[board] From 8a61865e4ad7d15cba44858b1b12c1bbf443cf68 Mon Sep 17 00:00:00 2001 From: Arnav Bajaj <49639514+Jackhammer9@users.noreply.github.com> Date: Wed, 12 Nov 2025 21:50:12 +0100 Subject: [PATCH 4/4] Update Readme.md --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index cf87301..aa38a73 100644 --- a/README.md +++ b/README.md @@ -155,10 +155,7 @@ At the moment two PlatformIO platforms are available: * `atmelavr` – classic AVR-based boards (Uno, Nano, Leonardo, etc.). * `atmelmegaavr` – newer megaAVR devices (Nano Every, Uno WiFi Rev2, Curiosity Nano kits, ...). -Every board listed in the [PlatformIO board registry for `atmelavr`](https://docs.platformio.org/en/latest/boards/atmelavr.html) -and [`atmelmegaavr`](https://docs.platformio.org/en/latest/boards/atmelmegaavr.html) can be targeted. If you choose an -unsupported board, or one that does not belong to the selected platform, `target()` raises -a `ValueError` with a helpful message. +Every board listed in the [PlatformIO board registry for all platforms](https://docs.platformio.org/en/latest/boards/index.html) can be targeted. If you choose an unsupported board, or one that does not belong to the selected platform, `target()` raises a `ValueError` with a helpful message. ```python from Reduino import target