共计 3545 个字符,预计需要花费 9 分钟才能阅读完成。
ESP32 在 IoT 领域的重要性
ESP32 作为一款低成本、低功耗的 Wi-Fi/ 蓝牙双模微控制器,已经成为物联网设备的首选芯片之一。它的强大之处在于:

- 双核处理器主频高达 240MHz,足以处理复杂的网络协议
- 内置 4MB Flash 和 520KB SRAM,能够满足大多数嵌入式应用的需求
- 丰富的 GPIO 接口,方便连接各种传感器和外设
在智能家居、工业自动化等场景中,为设备添加自然语言交互能力可以大幅提升用户体验。而 ChatGPT 这类大型语言模型的出现,让嵌入式设备也能实现智能对话功能。
HTTP REST 与 WebSocket 协议对比
在资源受限的 ESP32 上,选择合适的通信协议至关重要:
- HTTP REST
- 优点:实现简单,兼容性好,适合一次性请求 - 响应场景
- 缺点:每次请求都需要建立新连接,开销较大
-
内存占用:约 25-30KB(包含 TLS 开销)
-
WebSocket
- 优点:持久连接,适合频繁交互场景
- 缺点:实现复杂,内存占用较高
- 内存占用:约 35-40KB(包含 TLS 开销)
对于大多数 ChatGPT 应用场景,HTTP REST 已经足够,特别是当我们只需要偶尔发送用户查询时。
实战步骤详解
1. ESP32 的 WiFi 连接配置
可靠的 WiFi 连接是基础,我们需要实现自动重连机制:
// PlatformIO 项目结构中的主文件 main.cpp
#include <WiFi.h>
#define WIFI_SSID "your_SSID"
#define WIFI_PASS "your_password"
#define MAX_RETRIES 5
void connectWiFi() {WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
int retryCount = 0;
while (WiFi.status() != WL_CONNECTED && retryCount < MAX_RETRIES) {delay(1000);
Serial.println("Connecting to WiFi...");
retryCount++;
}
if(WiFi.status() == WL_CONNECTED) {Serial.println("WiFi connected");
Serial.print("IP address:");
Serial.println(WiFi.localIP());
} else {Serial.println("Failed to connect to WiFi");
// 这里可以添加进入低功耗模式或重启的逻辑
}
}
2. API 密钥的安全存储
使用 ESP32 的 NVS(Non-Volatile Storage)安全存储 API 密钥:
#include <Preferences.h>
Preferences preferences;
void setup() {preferences.begin("chatgpt", false);
// 首次使用时设置 API 密钥
// preferences.putString("api_key", "your_api_key");
String apiKey = preferences.getString("api_key", "");
if(apiKey.isEmpty()) {Serial.println("No API key found!");
}
}
3. 请求构造与响应解析
使用 ArduinoJSON 库处理 ChatGPT API 的 JSON 数据:
#include <ArduinoJson.h>
#include <HTTPClient.h>
void queryChatGPT(String prompt) {
HTTPClient http;
http.begin("https://api.openai.com/v1/chat/completions");
http.addHeader("Content-Type", "application/json");
http.addHeader("Authorization", "Bearer" + apiKey);
// 构造请求 JSON
DynamicJsonDocument requestDoc(1024);
requestDoc["model"] = "gpt-3.5-turbo";
JsonArray messages = requestDoc.createNestedArray("messages");
JsonObject systemMessage = messages.createNestedObject();
systemMessage["role"] = "system";
systemMessage["content"] = "You are a helpful assistant.";
JsonObject userMessage = messages.createNestedObject();
userMessage["role"] = "user";
userMessage["content"] = prompt;
String requestBody;
serializeJson(requestDoc, requestBody);
int httpCode = http.POST(requestBody);
if(httpCode == HTTP_CODE_OK) {String payload = http.getString();
// 解析响应
DynamicJsonDocument responseDoc(2048);
deserializeJson(responseDoc, payload);
String reply = responseDoc["choices"][0]["message"]["content"].as<String>();
Serial.println("Assistant:" + reply);
} else {Serial.printf("HTTP request failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();}
性能优化与生产环境建议
1. 内存管理
ESP32 的可用内存有限,需要特别注意:
- 使用
ESP.getFreeHeap()监控内存使用情况 - 对于大型 JSON 响应,考虑使用流式解析(Streaming)而非一次性加载
- 定期调用
heap_caps_print_heap_info(MALLOC_CAP_DEFAULT)检查堆碎片
2. 对话状态机设计
实现简单的状态机管理对话流程:
typedef enum {
STATE_IDLE,
STATE_WAITING_RESPONSE,
STATE_PROCESSING
} ChatState;
ChatState currentState = STATE_IDLE;
void loop() {switch(currentState) {
case STATE_IDLE:
// 等待用户输入
break;
case STATE_WAITING_RESPONSE:
// 处理网络响应
break;
case STATE_PROCESSING:
// 解析并显示响应
break;
}
}
3. 流式响应处理
对于长响应,使用环形缓冲区处理分块数据:
#include <CircularBuffer.h>
CircularBuffer<char, 1024> responseBuffer;
void handleChunkedResponse(String chunk) {for(int i=0; i<chunk.length(); i++) {responseBuffer.push(chunk[i]);
}
// 处理完整响应
if(chunk.indexOf("\n\n") != -1) {
String fullResponse;
while(!responseBuffer.isEmpty()) {fullResponse += responseBuffer.shift();
}
// 解析 fullResponse
}
}
扩展思考:离线唤醒方案
结合 ESP32 的双模特性,可以设计这样的工作流程:
- 在蓝牙低功耗 (BLE) 模式下监听唤醒词
- 检测到关键词后激活 Wi-Fi 连接
- 通过 Wi-Fi 发送查询到 ChatGPT API
- 获取响应后返回低功耗状态
这种方案可以大幅降低设备整体功耗,特别适合电池供电的场景。
总结
通过本文的实践,我们成功在 ESP32 上实现了 ChatGPT 的自然语言交互功能。关键点包括:可靠的 Wi-Fi 连接、安全的 API 密钥存储、高效的 JSON 处理以及谨慎的内存管理。这些技术不仅适用于 ChatGPT,也可以推广到其他云服务的集成中。
未来可以进一步探索模型量化技术,尝试在 ESP32 上本地运行小型语言模型,实现完全离线的语音交互能力。
正文完
