进阶:多环境管理与条件编译(Uno + ESP32)

知识库
知识库文档
/tech-stacks/platformio/examples/进阶:多环境管理与条件编译(Uno + ESP32).md

文档

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 更易读易维护

信息

路径
/tech-stacks/platformio/examples/进阶:多环境管理与条件编译(Uno + ESP32).md
更新时间
2026/5/31