跳转到内容

IDE间代码可移植性

本节介绍如何在 PlatformIO 和 Arduino IDE 之间移植 ESP32 代码。学完本节后,你将能够:

  • 理解 PlatformIO 和 Arduino IDE 项目的结构差异
  • 将 PlatformIO 多文件项目转换为 Arduino IDE 格式
  • 识别并修复常见的可移植性问题(函数原型、文件扩展名)
  • 维护一套可在两种环境中工作的代码库
  • PlatformIO 项目结构(01-05)
  • Arduino IDE 设置(01-04)

本课程的代码示例使用 PlatformIO(VS Code)开发,但一些用户更喜欢 Arduino IDE。这两种环境存在结构上的差异:

方面PlatformIOArduino IDE
主文件src/main.cppsketch_folder/sketch_name.ino
附加文件src/*.cpp, src/*.h, include/*.h同一文件夹中的 .ino 文件
库管理platformio.ini(声明式)库管理器(图形界面)
函数原型必需自动生成(非必需)
文件扩展名.cpp, .h.ino
配置platformio.ini工具 → 开发板菜单
platformio_project/
├── platformio.ini # 配置
├── src/
│ ├── main.cpp # 主代码
│ ├── wifi_mqtt.h # Wi-Fi/MQTT 代码(含实现的头文件)
│ └── credentials.h # SSID、密码
├── include/ # 额外的头文件
├── lib/ # 私有库
└── .pio/ # 构建产物
arduino_sketch/
├── sketch.ino # 主文件(与文件夹同名)
├── wifi_mqtt.ino # 附加代码标签页
├── credentials.ino # 凭据标签页
└── (无配置文件——设置在 IDE 菜单中)
  1. 找到你的 PlatformIO 项目文件夹
  2. 复制 src/ 目录的内容:
    • main.cpp
    • wifi_mqtt.h
    • credentials.h

步骤 2:创建 Arduino IDE 文件夹结构

Section titled “步骤 2:创建 Arduino IDE 文件夹结构”
  1. 创建一个新文件夹(例如 ESP32_Basic_Sketch
  2. 创建一个与文件夹同名的 .ino 文件:ESP32_Basic_Sketch.ino
  3. main.cpp 的内容复制到此 .ino 文件中

步骤 3:转换主文件(main.cpp → .ino)

Section titled “步骤 3:转换主文件(main.cpp → .ino)”

转换前(PlatformIO main.cpp)

#include <Arduino.h>
#include <WiFi.h>
#include "credentials.h"
#include "wifi_mqtt.h"
void setup() {
Serial.begin(115200);
// ...
}
void loop() {
// ...
}

转换后(Arduino IDE .ino)

#include <WiFi.h>
// PlatformIO 包含变为标准包含
// .h 文件需要转换为 .ino 标签页
void setup() {
Serial.begin(115200);
// ...
}
void loop() {
// ...
}

关键变更:

  • 移除 #include <Arduino.h>(Arduino IDE 会自动添加)
  • 移除 #include "credentials.h"——这些将变成独立的 .ino 标签页
  • 移除 #include "wifi_mqtt.h"——这也将变成独立的 .ino 标签页

步骤 4:将头文件转换为 .ino 标签页

Section titled “步骤 4:将头文件转换为 .ino 标签页”

wifi_mqtt.h → 在 Arduino IDE 中创建一个新标签页(Ctrl+Shift+N 或下拉箭头),命名为 wifi_mqtt.ino

// wifi_mqtt.ino — 粘贴 wifi_mqtt.h 的内容
#include <WiFi.h>
#include <PubSubClient.h>
WiFiClient wifiClient;
PubSubClient mqttClient(wifiClient);
void connectToWiFi() {
Serial.print("连接到 Wi-Fi");
WiFi.begin(ssid, password);
// ...
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
// ... 与 wifi_mqtt.h 相同的内容
}
void connectToMQTT() {
mqttClient.setServer(mqttServer, mqttPort);
mqttClient.setCallback(mqttCallback);
// ...
}

credentials.h → 创建另一个名为 credentials.ino 的标签页:

credentials.ino
const char* ssid = "YourWiFiSSID";
const char* password = "YourWiFiPassword";
const char* mqttServer = "192.168.1.100";
const int mqttPort = 1883;

步骤 5:处理函数原型(PlatformIO vs Arduino IDE)

Section titled “步骤 5:处理函数原型(PlatformIO vs Arduino IDE)”

PlatformIO 需要显式函数原型:

// PlatformIO:需要原型
void connectToWiFi();
void connectToMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);
void sendSensorData();
void setup() {
// ...
connectToWiFi(); // OK — 上面已声明原型
}
void connectToWiFi() {
// 实现
}

Arduino IDE 自动为 .ino 文件生成原型,因此显式原型是可选的:

// Arduino IDE:原型自动生成
// 你仍然可以添加它们,但不是必需的
void setup() {
connectToWiFi(); // OK — Arduino IDE 自动创建原型
}
void connectToWiFi() {
// 实现
}

跨平台兼容性,使用条件编译添加原型:

#ifdef PLATFORMIO
// PlatformIO 严格编译器需要显式原型
void connectToWiFi();
void connectToMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);
#endif

或者,将函数定义放在主文件的 setup() 之前:

// 这在两种环境中都有效——在 setup() 之前定义
void connectToWiFi() {
// 实现
}
void setup() {
connectToWiFi(); // 有效——函数已定义
}

步骤 6:更新 platformio.ini 依赖项——Arduino IDE 手动安装

Section titled “步骤 6:更新 platformio.ini 依赖项——Arduino IDE 手动安装”

PlatformIO 自动下载 lib_deps 中声明的库。在 Arduino IDE 中,必须通过库管理器手动安装。

platformio.ini(源)

lib_deps =
knolleary/PubSubClient @ ^2.8
bblanchon/ArduinoJson @ ^7.0
adafruit/DHT sensor library @ ^1.4

Arduino IDE 安装

  1. 工具 → 管理库(Ctrl+Shift+I)
  2. 搜索每个库名并点击安装
  3. 确保版本与 PlatformIO 规范匹配

对于在每个环境中行为必须不同的代码:

// 平台检测
#if defined(PLATFORMIO)
const char* projectVersion = "2.0.0 (PlatformIO)";
#elif defined(ARDUINO_ARCH_ESP32)
const char* projectVersion = "2.0.0 (Arduino IDE)";
#endif
void setup() {
Serial.begin(115200);
Serial.println(projectVersion);
#if defined(PLATFORMIO)
// PlatformIO 特定:可能不同的引脚映射
const int ledPin = 2;
#else
// Arduino IDE 特定回退
const int ledPin = 13; // 某些开发板的 LED 在引脚 13
#endif
pinMode(ledPin, OUTPUT);
}

步骤 8:Arduino IDE 转换完整检查清单

Section titled “步骤 8:Arduino IDE 转换完整检查清单”
  • #include <Arduino.h> 已移除(在 Arduino IDE 中会导致编译错误)
  • 所有 .h 文件已转换为 .ino 标签页(与主 .ino 在同一文件夹)
  • #include "filename.h" 已更改为匹配 .ino 标签页名称
  • 所需库已通过库管理器安装
  • 已正确选择开发板(工具 → 开发板 → ESP32 Arduino)
  • 已正确选择端口
  • 上传速度已设置(工具 → 上传速度 → 921600)
  • 草图编译无错误
  • 相同代码在 PlatformIO 和 Arduino IDE 中都能编译和运行
  • Arduino IDE 不报告”缺少函数原型”错误
  • 两种环境中的库版本匹配
  • 两个构建版本的串行输出相同
  • MQTT 和 Wi-Fi 行为相同

”Arduino.h: No such file or directory”

Section titled “”Arduino.h: No such file or directory””

原因:在 .ino 文件中使用了 #include <Arduino.h>

解决方案:从主 .ino 文件中移除 #include <Arduino.h>。Arduino IDE 会自动添加它。

“‘credentials_h’ was not declared in this scope”

Section titled ““‘credentials_h’ was not declared in this scope””

原因#include "credentials.h" 指令期望一个 .h 文件,但 Arduino IDE 标签页名为 credentials.ino

解决方案

  1. 将标签页重命名为 credentials.h(Arduino IDE 支持 .h 标签页)
  2. 或者移除 #include——Arduino IDE 会自动包含草图文件夹中的所有 .ino 标签页
  3. 或者使用 #include "credentials.ino"(不太常规但有效)

在 PlatformIO 中出现”函数未在此作用域中声明”但在 Arduino IDE 中正常

Section titled “在 PlatformIO 中出现”函数未在此作用域中声明”但在 Arduino IDE 中正常”

原因:PlatformIO 要求在首次使用前声明函数原型。

解决方案:在 main.cpp 顶部添加原型:

// 在 setup() 之前添加这些
void connectToWiFi();
void connectToMQTT();
void mqttCallback(char* topic, byte* payload, unsigned int length);

或者重新排列函数,使被调用者出现在调用者之前。

  • 在 PlatformIO 中开发,在 Arduino IDE 中测试:PlatformIO 更快且工具更好,但需验证可移植性
  • 谨慎使用条件编译:编写兼容代码比到处使用 #ifdef 更好
  • 保持文件结构一致:如果 PlatformIO 项目有 credentials.h,在 Arduino IDE 中也创建 credentials.h 标签页(而不是 .ino
  • 记录库版本:记录 PlatformIO 中使用的库版本,以便在 Arduino IDE 中匹配
  • 及早测试,频繁测试:在重大代码变更后,验证草图在两种环境中都能编译
  • 在 Arduino IDE 标签页中使用 .h 扩展名:Arduino IDE 支持 .h 文件作为标签页,使转换更自然
  1. PlatformIO 使用 src/main.cpp + *.h 文件;Arduino IDE 使用单个文件夹中的 .ino 文件
  2. 主要转换步骤:将 main.cpp 重命名为 .ino,将 *.h 转换为 .ino.h 标签页
  3. 为 Arduino IDE 移除 #include <Arduino.h>#include "filename.h"
  4. 为 PlatformIO 更严格的编译器添加函数原型
  5. 在 Arduino IDE 中手动安装 PlatformIO 自动下载的库
  6. PlatformIO 提供更快的编译和更好的代码组织;Arduino IDE 提供简洁性和广泛兼容性