文档
PlatformIO 多环境管理:ESP32 + Arduino Uno 双目标构建
目标
一个 PlatformIO 项目同时支持 ESP32 和 Arduino Uno,通过环境配置和条件编译实现跨平台代码复用。
项目结构
multi-target/
├── platformio.ini
├── src/
│ └── main.cpp
├── include/
│ └── board_config.h
└── lib/
└── (自定义库)
一、platformio.ini — 多环境配置
; platformio.ini
[platformio]
default_envs = uno ; 默认环境
; ── 公共配置(所有环境继承) ──
[common]
monitor_speed = 115200
lib_deps =
adafruit/Adafruit Unified Sensor @ ^1.1.14
; ── Arduino Uno 环境 ──
[env:uno]
platform = atmelavr
board = uno
framework = arduino
build_flags =
-D BOARD_UNO
-D LED_PIN=LED_BUILTIN
lib_deps =
${common.lib_deps}
adafruit/DHT sensor library @ ^1.4.4
monitor_speed = 9600
; ── ESP32 DevKit 环境 ──
[env:esp32]
platform = espressif32
board = esp32dev
framework = arduino
build_flags =
-D BOARD_ESP32
-D LED_PIN=2
lib_deps =
${common.lib_deps}
adafruit/DHT sensor library @ ^1.4.4
adafruit/Adafruit SSD1306 @ ^2.5.7
adafruit/Adafruit GFX Library @ ^1.11.9
monitor_filters = esp32_exception_decoder
; ── ESP32-C3 环境 ──
[env:esp32-c3]
platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
build_flags =
-D BOARD_ESP32_C3
-D LED_PIN=8
lib_deps =
${common.lib_deps}
adafruit/DHT sensor library @ ^1.4.4
二、board_config.h — 条件编译板级配置
// board_config.h
#pragma once
#include <Arduino.h>
// ── 板级识别 ──
#if defined(BOARD_UNO)
#define BOARD_NAME "Arduino Uno"
#define CPU_SPEED_MHZ 16
#define HAS_WIFI 0
#define HAS_BLUETOOTH 0
#define RAM_KB 2
#define FLASH_KB 32
#elif defined(BOARD_ESP32)
#define BOARD_NAME "ESP32 DevKit"
#define CPU_SPEED_MHZ 240
#define HAS_WIFI 1
#define HAS_BLUETOOTH 1
#define RAM_KB 520
#define FLASH_KB 4096
#elif defined(BOARD_ESP32_C3)
#define BOARD_NAME "ESP32-C3"
#define CPU_SPEED_MHZ 160
#define HAS_WIFI 1
#define HAS_BLUETOOTH 1
#define RAM_KB 400
#define FLASH_KB 4096
#else
#error "未定义目标板!请使用 -D BOARD_xxx 编译宏"
#endif
// ── 引脚映射表 ──
#if defined(BOARD_UNO)
#define PIN_I2C_SDA A4
#define PIN_I2C_SCL A5
#define PIN_DHT 2
#define PIN_BUTTON 3
#elif defined(BOARD_ESP32)
#define PIN_I2C_SDA 21
#define PIN_I2C_SCL 22
#define PIN_DHT 4
#define PIN_BUTTON 0 // BOOT 按键
#elif defined(BOARD_ESP32_C3)
#define PIN_I2C_SDA 6
#define PIN_I2C_SCL 7
#define PIN_DHT 3
#define PIN_BUTTON 9
#endif
三、main.cpp — 跨平台业务逻辑
#include <Arduino.h>
#include "board_config.h"
#if HAS_WIFI
#include <WiFi.h>
#endif
// ── 启动信息 ──
void printBoardInfo() {
Serial.println(F("========================"));
Serial.print(F("板卡: "));
Serial.println(BOARD_NAME);
Serial.print(F("CPU: "));
Serial.print(CPU_SPEED_MHZ);
Serial.println(F(" MHz"));
Serial.print(F("RAM: "));
Serial.print(RAM_KB);
Serial.print(F(" KB | Flash: "));
Serial.print(FLASH_KB);
Serial.println(F(" KB"));
Serial.print(F("WiFi: "));
Serial.println(HAS_WIFI ? F("✓") : F("✗"));
Serial.print(F("BLE: "));
Serial.println(HAS_BLUETOOTH ? F("✓") : F("✗"));
Serial.print(F("LED: D"));
Serial.print(LED_PIN);
Serial.print(F(" | DHT: D"));
Serial.print(PIN_DHT);
Serial.println(F("\n========================"));
}
// ── 统一延时(毫秒) ──
void smartDelay(unsigned long ms) {
unsigned long start = millis();
while (millis() - start < ms) {
#if HAS_WIFI
// ESP32 还需要处理 WiFi 和其他后台任务
// 非 ESP32 上这行在编译时被去除
#endif
delay(1); // 让步给系统
}
}
// ── 初始化 ──
void setup() {
Serial.begin(115200);
// 某些 ESP32-C3 需要延时等待 USB CDC 就绪
#if defined(BOARD_ESP32_C3)
delay(1000);
#endif
while (!Serial && millis() < 3000) {
delay(10);
}
printBoardInfo();
pinMode(LED_PIN, OUTPUT);
pinMode(PIN_BUTTON, INPUT_PULLUP);
#if HAS_WIFI
WiFi.mode(WIFI_OFF); // 省电
Serial.println(F("WiFi 已关闭(省电模式)"));
#endif
Serial.println(F("\n系统就绪!"));
Serial.print(F("LED 开始闪烁 | 按键引脚 D"));
Serial.println(PIN_BUTTON);
}
// ── 主循环 ──
void loop() {
static unsigned long lastBlink = 0;
static unsigned long lastReport = 0;
static uint32_t loopCount = 0;
loopCount++;
// LED 闪烁
if (millis() - lastBlink >= 500) {
lastBlink = millis();
digitalWrite(LED_PIN, !digitalRead(LED_PIN));
}
// 状态报告
if (millis() - lastReport >= 5000) {
lastReport = millis();
Serial.print(F("["));
Serial.print(BOARD_NAME);
Serial.print(F("] 运行中 | 循环 #"));
Serial.print(loopCount);
Serial.print(F(" | 运行时间: "));
Serial.print(millis() / 1000);
Serial.println(F("s"));
#if (FLASH_KB > 100)
Serial.print(F(" 可用堆: "));
Serial.print(ESP.getFreeHeap());
Serial.println(F(" bytes"));
#endif
}
// 按键检测(可选)
if (digitalRead(PIN_BUTTON) == LOW) {
delay(20);
if (digitalRead(PIN_BUTTON) == LOW) {
Serial.println(F("按键按下!"));
while (digitalRead(PIN_BUTTON) == LOW) { delay(10); }
}
}
}
四、构建与烧录
# 列出所有环境
pio project config
# 构建指定环境
pio run -e uno
pio run -e esp32
pio run -e esp32-c3
# 一键构建全部
pio run
# 烧录(自动检测端口)
pio run -e esp32 -t upload
# 监控
pio device monitor -e esp32
关键点
${common.xxx}语法实现配置继承,避免重复-D MACRO编译标志注入板级宏,驱动#if条件编译board_config.h集中管理引脚映射,业务逻辑代码零#ifdef#if HAS_WIFI这种语义化条件比#ifdef ESP32更易读易维护