Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0541226
retry when transient HTTP errors occur (#463)
Jason2866 Apr 12, 2026
5b87412
fix: reduce double Tcl braces to single braces in program_esp command…
Jason2866 Apr 13, 2026
5a7c835
Skip FileDownloader monkey-patch when native retry support exists (#467)
Jason2866 Apr 13, 2026
a852731
Refactor GitHub Actions workflow for examples
Jason2866 Apr 13, 2026
a1774b9
Load pioarduino dep from PyPi
Jason2866 Apr 13, 2026
9a92d09
remove not anymore needed version check from URL via regex
Jason2866 Apr 13, 2026
fb230b1
feature: Use espressif modified clangd tool (#476)
Jason2866 Apr 14, 2026
c33539d
fix: add missing -DBOARD_HAS_PSRAM to qio_opi boards (#478)
coderabbitai[bot] Apr 15, 2026
c1113ed
Refactor: Hybrid Compile sdkconfig handling (#477)
Jason2866 Apr 15, 2026
f89457d
Add condition for linker script based on MCU type
Jason2866 Apr 15, 2026
1d79f1b
Revise README to reflect support status change
Jason2866 Apr 15, 2026
6bbbb60
Update platform version to 55.03.38+develop
Jason2866 Apr 17, 2026
1d8ba51
Update installation instructions for pioarduino IDE
Jason2866 Apr 29, 2026
abf32c2
Update package versions for openocd and clangd tools (#485)
Jason2866 Apr 30, 2026
33be7fb
fix #480 as suggested (#487)
Jason2866 May 3, 2026
568534b
add C5 / C61 svd xml
Jason2866 May 3, 2026
a56c23a
Pio lock file https://github.com/m-mcgowan/pio-lock (#489)
Jason2866 May 6, 2026
10b02a5
Update Arduino and IDF version in README
Jason2866 May 6, 2026
bbbcbd5
Update README to clarify Python requirement
Jason2866 May 6, 2026
bf0f8da
Update README.md
Jason2866 May 6, 2026
919bb0c
Enhance Filesystem partition detection (#490)
Jason2866 May 6, 2026
96d1677
LittleFS: Format FS before mount to ensure a correct superblock
Jason2866 May 6, 2026
ee7fd43
Add support for user-specified filesystem partition (#491)
Jason2866 May 11, 2026
979e744
Update debugger package versions in platform.json (#492)
Jason2866 May 13, 2026
852914a
log_printf wrapping removal in build flags (#497)
Jason2866 May 25, 2026
fac5352
HybridCompile: Exit after Arduino project SCons run (#498)
Jason2866 May 25, 2026
c87c661
Forward targets to Arduino HybridCompile run (#499)
Jason2866 May 25, 2026
c783450
Arduino v3.3.9
Jason2866 Jun 4, 2026
7f7c64d
esptool v5.3.0
Jason2866 Jun 4, 2026
dc40a78
Merge branch 'main' into rel_339
Jason2866 Jun 4, 2026
1761124
fix merge errors
Jason2866 Jun 4, 2026
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
4 changes: 1 addition & 3 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
with:
submodules: "recursive"
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand All @@ -66,7 +64,7 @@ jobs:
enable-cache: false
- name: Install dependencies
run: |
uv pip install --system -U https://github.com/pioarduino/platformio-core/archive/refs/tags/v6.1.19.zip
uv pip install --system pioarduino
pio pkg install --global --platform file://.
- name: git clone Tasmota and add to examples
if: "matrix.example == 'examples/tasmota'"
Expand Down
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ Prerequisites:
## Installation

### VSCode Extension
- [Download and install Microsoft Visual Studio Code](https://code.visualstudio.com/). pioarduino IDE is on top of it.
- Download and install [Microsoft Visual Studio Code](https://code.visualstudio.com/). [pioarduino IDE](https://marketplace.visualstudio.com/items?itemName=pioarduino.pioarduino-ide) is on top of it.
- Open the extension manager.
- Search for the `pioarduino ide` extension.
- Install pioarduino IDE extension.
- Install [pioarduino IDE](https://marketplace.visualstudio.com/items?itemName=pioarduino.pioarduino-ide) extension.

### CLI
```bash
Expand Down Expand Up @@ -58,10 +58,6 @@ pioarduino provides native support for multiple filesystem options, allowing you
- **SPIFFS** - Simple legacy filesystem. While still functional, LittleFS is recommended for new projects due to better wear-leveling and reliability.
- **FatFS** - Industry-standard FAT filesystem with broad compatibility across platforms and operating systems.

### FatFS Integration

FatFS support has been fully integrated as a Python module, providing the same seamless experience as LittleFS. Configuration is straightforward - simply specify your preferred filesystem in your project settings: See [FATFS_INTEGRATION.md](FATFS_INTEGRATION.md) for detailed documentation.

**Quick Start:**

```ini
Expand All @@ -80,7 +76,7 @@ pio run -t download_fatfs # Download and extract FatFS from device
See the [arduino-fatfs example](examples/arduino-fatfs/) for a complete working example.

### Stable Arduino
currently espressif Arduino 3.3.8 and IDF v5.5.4
currently espressif Arduino 3.3.9 and IDF v5.5.4.

```ini
[env:stable]
Expand Down
1 change: 1 addition & 0 deletions boards/atd147_s3.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_ATD143_S3",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
Expand Down
1 change: 1 addition & 0 deletions boards/dfrobot_romeo_esp32s3.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_DFROBOT_ROMEO_ESP32S3",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
Expand Down
1 change: 1 addition & 0 deletions boards/lilygo-t-display-s3.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_LILYGO_T_DISPLAY_S3",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
Expand Down
1 change: 1 addition & 0 deletions boards/m5stack-atoms3u.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_M5Stack_ATOMS3U",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
Expand Down
1 change: 1 addition & 0 deletions boards/nebulas3.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"core": "esp32",
"extra_flags": [
"-DARDUINO_NEBULAS3",
"-DBOARD_HAS_PSRAM",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1",
Expand Down
7 changes: 7 additions & 0 deletions builder/frameworks/arduino.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ def has_picolibc_config():

build_unflags = " ".join(env['BUILD_UNFLAGS'])

# -Wl,--wrap=log_printf: remove always. Diagnostics is not supported with HybridCompile
build_unflags += " -Wl,--wrap=log_printf"

# -mdisable-hardware-atomics: always for solo1, or when PSRAM is NOT configured
if has_unicore_flags() or not has_psram_config():
build_unflags += " -mdisable-hardware-atomics"
Expand All @@ -357,6 +360,10 @@ def has_picolibc_config():
new_build_unflags = build_unflags.split()
env.Replace(BUILD_UNFLAGS=new_build_unflags)

# add linker script esp32.rom.libc-funcs.ld for esp32 when PSRAM is NOT configured
if mcu == "esp32" and not has_psram_config():
env.Append(LINKFLAGS=["-T", "esp32.rom.libc-funcs.ld"])


def get_MD5_hash(phrase):
return hashlib.md5(phrase.encode('utf-8')).hexdigest()[:16]
Expand Down
175 changes: 126 additions & 49 deletions builder/frameworks/espidf.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@
config = env.GetProjectConfig()
board = env.BoardConfig()
pio_orig_frwrk = env.GetProjectOption("framework")
mcu = board.get("build.mcu", "esp32")
mcu = board.get("build.mcu", None)
if not mcu:
sys.stderr.write("Error: Missing required board manifest field 'build.mcu'\n")
env.Exit(1)
chip_variant = board.get("build.chip_variant", "").lower()
chip_variant = chip_variant if chip_variant else mcu
flash_speed = board.get("build.f_flash", "40000000L")
flash_frequency = str(flash_speed.replace("000000L", ""))
flash_mode = board.get("build.flash_mode", "dio")
flash_mode = board.get("build.flash_mode", None)
boot_mode = board.get("build.boot", None)
idf_variant = mcu.lower()
flag_custom_sdkonfig = False
Expand Down Expand Up @@ -154,6 +157,39 @@ def create_silent_action(action_func):
silent_action.strfunction = lambda target, source, env: ''
return silent_action


def get_requested_cli_targets():
"""Return requested PlatformIO targets, with sys.argv fallback for IDE runs."""
targets = [str(t).strip() for t in COMMAND_LINE_TARGETS if str(t).strip()]
if targets:
return targets

# In some IDE-triggered invocations (e.g. VS Code), COMMAND_LINE_TARGETS
# can be empty during script loading, so parse raw argv as a fallback.
argv = [str(arg) for arg in sys.argv]
parsed_targets = []
i = 0
while i < len(argv):
arg = argv[i]
if arg in ("-t", "--target"):
if i + 1 < len(argv):
parsed_targets.append(argv[i + 1])
i += 1
elif arg.startswith("--target="):
parsed_targets.append(arg.split("=", 1)[1])
elif arg.startswith("-t") and arg != "-t":
parsed_targets.append(arg[2:])
i += 1

normalized = []
seen = set()
for target in parsed_targets:
cleaned = str(target).strip().strip('"\'')
if cleaned and cleaned not in seen:
seen.add(cleaned)
normalized.append(cleaned)
return normalized

if "arduino" in env.subst("$PIOFRAMEWORK"):
_arduino_pkg_dir = platform.get_package_dir("framework-arduinoespressif32")
if not _arduino_pkg_dir or not os.path.isdir(_arduino_pkg_dir):
Expand Down Expand Up @@ -240,10 +276,25 @@ def contains_path_traversal(url):

# Check for board-specific configurations that require sdkconfig generation
def has_board_specific_config():
"""Check if board has configuration that needs to be applied to sdkconfig."""
"""Check if board has configuration that needs to be applied to sdkconfig.

Returns True when any board manifest field would produce sdkconfig flags,
including flash mode, CPU frequency, flash size, memory type, or PSRAM.
"""
# Always true when basic board build fields exist (flash mode, f_cpu, flash size, etc.)
if board.get("build.f_cpu", None) or board.get("build.f_flash", None):
return True
if flash_mode:
return True
if board.get("upload", {}).get("flash_size", None):
return True

# Check for PSRAM support
extra_flags = board.get("build.extra_flags", [])
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)
if isinstance(extra_flags, str):
has_psram = "-DBOARD_HAS_PSRAM" in extra_flags
else:
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)

# Check for special memory types
memory_type = None
Expand Down Expand Up @@ -435,6 +486,28 @@ def generate_board_specific_config():
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHSIZE=\"{flash_size}\"")
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHSIZE_{flash_size}=y")

# Check for PSRAM support based on board flags (needed before frequency config)
extra_flags = board.get("build.extra_flags", "")
# Handle both string and list formats
if isinstance(extra_flags, str):
has_psram = "-DBOARD_HAS_PSRAM" in extra_flags
else:
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)

# Additional PSRAM detection methods
if not has_psram:
# Check if memory_type contains psram indicators
if memory_type and ("opi" in memory_type.lower() or "psram" in memory_type.lower()):
has_psram = True
# Check build.psram_type
elif "psram_type" in board.get("build", {}):
has_psram = True
# Check for SPIRAM mentions in extra_flags
elif isinstance(extra_flags, str) and "PSRAM" in extra_flags:
has_psram = True
elif not isinstance(extra_flags, str) and any("PSRAM" in str(flag) for flag in extra_flags):
has_psram = True

# Handle Flash and PSRAM frequency configuration with platformio.ini override support
# Priority: platformio.ini > board.json manifest
# From 80MHz onwards, Flash and PSRAM frequencies must be identical
Expand Down Expand Up @@ -531,44 +604,23 @@ def generate_board_specific_config():
if mcu == "esp32p4":
board_config_flags.append(f"CONFIG_ESPTOOLPY_FLASHFREQ_VAL={flash_freq_val}")

# Configure PSRAM frequency
# Disable other SPIRAM speed options first
psram_freqs = ["20", "40", "80", "120", "200"]
for freq in psram_freqs:
if freq != psram_freq_str:
board_config_flags.append(f"# CONFIG_SPIRAM_SPEED_{freq}M is not set")
# Then set the specific SPIRAM configs
board_config_flags.append(f"CONFIG_SPIRAM_SPEED={psram_freq_str}")
board_config_flags.append(f"CONFIG_SPIRAM_SPEED_{psram_freq_str}M=y")
# Configure PSRAM frequency only if board has PSRAM
if has_psram:
# Disable other SPIRAM speed options first
psram_freqs = ["20", "40", "80", "120", "200"]
for freq in psram_freqs:
if freq != psram_freq_str:
board_config_flags.append(f"# CONFIG_SPIRAM_SPEED_{freq}M is not set")
# Then set the specific SPIRAM configs
board_config_flags.append(f"CONFIG_SPIRAM_SPEED={psram_freq_str}")
board_config_flags.append(f"CONFIG_SPIRAM_SPEED_{psram_freq_str}M=y")

# Enable experimental features for Flash frequencies > 80MHz
if flash_freq_val > 80:
board_config_flags.append("CONFIG_IDF_EXPERIMENTAL_FEATURES=y")
board_config_flags.append("CONFIG_SPI_FLASH_HPM_ENABLE=y")
board_config_flags.append("CONFIG_SPI_FLASH_HPM_AUTO=y")

# Check for PSRAM support based on board flags
extra_flags = board.get("build.extra_flags", "")
# Handle both string and list formats
if isinstance(extra_flags, str):
has_psram = "-DBOARD_HAS_PSRAM" in extra_flags
else:
has_psram = any("-DBOARD_HAS_PSRAM" in flag for flag in extra_flags)

# Additional PSRAM detection methods
if not has_psram:
# Check if memory_type contains psram indicators
if memory_type and ("opi" in memory_type.lower() or "psram" in memory_type.lower()):
has_psram = True
# Check build.psram_type
elif "psram_type" in board.get("build", {}):
has_psram = True
# Check for SPIRAM mentions in extra_flags
elif isinstance(extra_flags, str) and "PSRAM" in extra_flags:
has_psram = True
elif not isinstance(extra_flags, str) and any("PSRAM" in str(flag) for flag in extra_flags):
has_psram = True

if has_psram:
# Enable basic SPIRAM support
board_config_flags.append("CONFIG_SPIRAM=y")
Expand Down Expand Up @@ -638,6 +690,11 @@ def generate_board_specific_config():
board_config_flags.extend([
"# CONFIG_SPIRAM is not set"
])
if mcu == "esp32":
board_config_flags.extend([
"# CONFIG_BOOTLOADER_SPI_CUSTOM_WP_PIN is not set",
"CONFIG_BOOTLOADER_SPI_WP_PIN=7"
])

# Use flash_memory_type for flash config
if flash_memory_type and "opi" in flash_memory_type.lower():
Expand Down Expand Up @@ -734,17 +791,26 @@ def write_sdkconfig_file(idf_config_flags, checksum_source):
continue

# Check if we have a custom replacement for this flag
# Search from the end so that later entries (user overrides) win
flag_replaced = False
for custom_flag in idf_config_flags[:]: # Create copy for safe removal
custom_flag_name = extract_flag_name(custom_flag.replace("'", ""))

last_match_idx = None
for idx in range(len(idf_config_flags) - 1, -1, -1):
custom_flag_name = extract_flag_name(idf_config_flags[idx].replace("'", ""))
if flag_name == custom_flag_name:
cleaned_flag = custom_flag.replace("'", "")
dst.write(cleaned_flag + "\n")
print(f"Replace: {line.strip()} with: {cleaned_flag}")
idf_config_flags.remove(custom_flag)
flag_replaced = True
break
if last_match_idx is None:
last_match_idx = idx
else:
# Remove earlier duplicate (lower priority)
idf_config_flags.pop(idx)
if last_match_idx > idx:
last_match_idx -= 1

if last_match_idx is not None:
custom_flag = idf_config_flags.pop(last_match_idx)
cleaned_flag = custom_flag.replace("'", "")
dst.write(cleaned_flag + "\n")
print(f"Replace: {line.strip()} with: {cleaned_flag}")
flag_replaced = True

if not flag_replaced:
dst.write(line)
Expand Down Expand Up @@ -2960,12 +3026,18 @@ def _replace_copy(src, dst):
PYTHON_EXE = env.subst("$PYTHONEXE")
pio_exe_path = str(Path(os.path.dirname(PYTHON_EXE)) / ("pio" + (".exe" if IS_WINDOWS else "")))
pio_cmd = env["PIOENV"]
env.Execute(
child_targets = [t for t in get_requested_cli_targets() if t != "checkprogsize"]
child_target_args = " ".join(f'-t "{target}"' for target in child_targets)
child_run_cmd = (
f'"{pio_exe_path}" run -e "{pio_cmd}" {child_target_args}'.strip()
)
if int(ARGUMENTS.get("PIOVERBOSE", 0)):
forwarded = ", ".join(child_targets) if child_targets else "(none)"
print(f"[HybridCompile] Forwarding child targets: {forwarded}")
print(f"[HybridCompile] Child command: {child_run_cmd}")
child_rc = env.Execute(
env.VerboseAction(
(
'"%s" run -e ' % pio_exe_path
+ " ".join(['"%s"' % pio_cmd])
),
child_run_cmd,
"*** Starting Arduino compile %s with custom libraries ***" % pio_cmd,
)
)
Expand All @@ -2979,6 +3051,11 @@ def _replace_copy(src, dst):
from component_manager import ComponentManager
component_manager = ComponentManager(env)
component_manager.restore_pioarduino_build_py()

# The child `pio run` already performs the full Arduino build in a
# fully configured environment. Stop here to avoid re-running binary
# post-actions in the outer, partially configured SCons environment.
env.Exit(child_rc if child_rc else 0)
silent_action = create_silent_action(idf_lib_copy)
env.AddPostAction("checkprogsize", silent_action)

Expand Down
Loading