硬件技术文章

"ESP32 WiFi开发与物联网应用"

# ESP32 WiFi开发与物联网应用

ESP32是一款功能强大的WiFi和蓝牙双模微控制器,为物联网项目提供了理想的解决方案。本文将全面介绍ESP32的WiFi开发技术,从基础连接到高级物联网应用。

## ESP32 WiFi基础

ESP32内置WiFi模块,支持802.11 b/g/n协议,可以工作在Station模式、AP模式或混合模式。

### 开发环境设置

```cpp
// 必要的库文件
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <PubSubClient.h>
```

### WiFi连接基础

```cpp
const char* ssid = "your_wifi_name";
const char* password = "your_wifi_password";

void setup() {
  Serial.begin(115200);
  
  // 初始化WiFi
  WiFi.begin(ssid, password);
  
  // 等待连接
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("连接WiFi中...");
  }
  
  Serial.println("WiFi连接成功");
  Serial.print("IP地址: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // 检查连接状态
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi连接丢失,尝试重连...");
    WiFi.reconnect();
  }
  
  delay(5000);
}
```

## Web服务器开发

### 基础Web服务器

```cpp
#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "your_wifi_name";
const char* password = "your_wifi_password";

WebServer server(80);

void setup() {
  Serial.begin(115200);
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("连接中...");
  }
  
  Serial.println("WiFi连接成功");
  Serial.print("IP地址: ");
  Serial.println(WiFi.localIP());
  
  // 设置路由
  server.on("/", handleRoot);
  server.on("/api/status", handleStatus);
  server.on("/api/control", handleControl);
  
  // 启动服务器
  server.begin();
  Serial.println("Web服务器启动");
}

void loop() {
  server.handleClient();
}

void handleRoot() {
  String html = "<!DOCTYPE html><html><head><title>ESP32控制面板</title></head>";
  html += "<body><h1>ESP32控制面板</h1>";
  html += "<p>状态: <span id='status'>获取中...</span></p>";
  html += "<button onclick='toggleLED()'>切换LED</button>";
  html += "<script>";
  html += "function toggleLED() {";
  html += "  fetch('/api/control?action=toggle')";
  html += "    .then(response => response.json())";
  html += "    .then(data => document.getElementById('status').innerHTML = data.status);";
  html += "}";
  html += "</script></body></html>";
  
  server.send(200, "text/html", html);
}

void handleStatus() {
  StaticJsonDocument<200> doc;
  doc["status"] = "online";
  doc["uptime"] = millis();
  doc["free_heap"] = ESP.getFreeHeap();
  
  String response;
  serializeJson(doc, response);
  server.send(200, "application/json", response);
}

void handleControl() {
  String action = server.arg("action");
  
  if (action == "toggle") {
    // 切换LED状态
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    
    StaticJsonDocument<200> doc;
    doc["status"] = digitalRead(LED_BUILTIN) ? "LED开启" : "LED关闭";
    
    String response;
    serializeJson(doc, response);
    server.send(200, "application/json", response);
  } else {
    server.send(400, "text/plain", "无效的操作");
  }
}
```

### 高级Web服务器功能

```cpp
#include <WiFi.h>
#include <WebServer.h>
#include <SPIFFS.h>

WebServer server(80);

void setup() {
  Serial.begin(115200);
  
  // 初始化SPIFFS文件系统
  if (!SPIFFS.begin(true)) {
    Serial.println("SPIFFS初始化失败");
    return;
  }
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("连接中...");
  }
  
  // 设置路由
  server.on("/", handleRoot);
  server.on("/api/sensors", handleSensors);
  server.on("/api/config", HTTP_GET, handleGetConfig);
  server.on("/api/config", HTTP_POST, handlePostConfig);
  server.onNotFound(handleNotFound);
  
  // 启用CORS
  server.enableCORS(true);
  
  server.begin();
  Serial.println("Web服务器启动");
}

void handleRoot() {
  // 从SPIFFS读取HTML文件
  File file = SPIFFS.open("/index.html", "r");
  if (!file) {
    server.send(404, "text/plain", "文件未找到");
    return;
  }
  
  server.streamFile(file, "text/html");
  file.close();
}

void handleSensors() {
  StaticJsonDocument<500> doc;
  
  // 模拟传感器数据
  doc["temperature"] = random(20, 30);
  doc["humidity"] = random(40, 80);
  doc["light"] = random(100, 1000);
  doc["timestamp"] = millis();
  
  String response;
  serializeJson(doc, response);
  server.send(200, "application/json", response);
}

void handleGetConfig() {
  File file = SPIFFS.open("/config.json", "r");
  if (!file) {
    server.send(404, "text/plain", "配置文件未找到");
    return;
  }
  
  server.streamFile(file, "application/json");
  file.close();
}

void handlePostConfig() {
  if (server.hasArg("plain")) {
    String config = server.arg("plain");
    
    File file = SPIFFS.open("/config.json", "w");
    if (file) {
      file.print(config);
      file.close();
      server.send(200, "application/json", "{\"status\":\"success\"}");
    } else {
      server.send(500, "application/json", "{\"status\":\"error\"}");
    }
  } else {
    server.send(400, "application/json", "{\"status\":\"bad_request\"}");
  }
}

void handleNotFound() {
  server.send(404, "text/plain", "页面未找到");
}
```

## MQTT通信

### MQTT客户端实现

```cpp
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "your_wifi_name";
const char* password = "your_wifi_password";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {
  Serial.begin(115200);
  
  // 连接WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("连接WiFi中...");
  }
  
  // 设置MQTT服务器
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(mqttCallback);
  
  // 连接MQTT
  connectMQTT();
}

void loop() {
  if (!client.connected()) {
    connectMQTT();
  }
  client.loop();
  
  // 发布传感器数据
  publishSensorData();
  delay(5000);
}

void connectMQTT() {
  while (!client.connected()) {
    Serial.print("连接MQTT服务器...");
    
    if (client.connect("ESP32Client")) {
      Serial.println("连接成功");
      
      // 订阅主题
      client.subscribe("esp32/commands");
      client.subscribe("esp32/settings");
    } else {
      Serial.print("连接失败,错误代码: ");
      Serial.print(client.state());
      Serial.println(" 5秒后重试");
      delay(5000);
    }
  }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  
  Serial.print("收到消息 [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);
  
  // 处理不同的主题
  if (String(topic) == "esp32/commands") {
    handleCommand(message);
  } else if (String(topic) == "esp32/settings") {
    handleSettings(message);
  }
}

void handleCommand(String command) {
  if (command == "led_on") {
    digitalWrite(LED_BUILTIN, HIGH);
    client.publish("esp32/status", "LED开启");
  } else if (command == "led_off") {
    digitalWrite(LED_BUILTIN, LOW);
    client.publish("esp32/status", "LED关闭");
  } else if (command == "restart") {
    client.publish("esp32/status", "重启中...");
    ESP.restart();
  }
}

void handleSettings(String settings) {
  StaticJsonDocument<200> doc;
  deserializeJson(doc, settings);
  
  if (doc.containsKey("update_interval")) {
    int interval = doc["update_interval"];
    // 更新发送间隔
    Serial.println("更新间隔设置为: " + String(interval) + "ms");
  }
}

void publishSensorData() {
  StaticJsonDocument<200> doc;
  doc["temperature"] = random(20, 30);
  doc["humidity"] = random(40, 80);
  doc["timestamp"] = millis();
  
  String payload;
  serializeJson(doc, payload);
  
  client.publish("esp32/sensors", payload.c_str());
}
```

### 高级MQTT功能

```cpp
class MQTTManager {
private:
  PubSubClient* client;
  String deviceId;
  String baseTopic;
  unsigned long lastReconnectAttempt = 0;
  const unsigned long reconnectInterval = 5000;
  
public:
  MQTTManager(PubSubClient* mqttClient, String id) {
    client = mqttClient;
    deviceId = id;
    baseTopic = "devices/" + deviceId;
  }
  
  bool connect() {
    if (client->connected()) {
      return true;
    }
    
    unsigned long now = millis();
    if (now - lastReconnectAttempt > reconnectInterval) {
      lastReconnectAttempt = now;
      
      if (client->connect(deviceId.c_str())) {
        Serial.println("MQTT连接成功");
        
        // 订阅设备特定主题
        client->subscribe((baseTopic + "/commands").c_str());
        client->subscribe((baseTopic + "/settings").c_str());
        
        // 发布设备上线消息
        publishStatus("online");
        
        return true;
      } else {
        Serial.print("MQTT连接失败,错误代码: ");
        Serial.println(client->state());
      }
    }
    
    return false;
  }
  
  void publishStatus(String status) {
    StaticJsonDocument<100> doc;
    doc["status"] = status;
    doc["timestamp"] = millis();
    doc["uptime"] = millis();
    
    String payload;
    serializeJson(doc, payload);
    
    client->publish((baseTopic + "/status").c_str(), payload.c_str());
  }
  
  void publishSensorData(float temperature, float humidity, float light) {
    StaticJsonDocument<200> doc;
    doc["temperature"] = temperature;
    doc["humidity"] = humidity;
    doc["light"] = light;
    doc["timestamp"] = millis();
    
    String payload;
    serializeJson(doc, payload);
    
    client->publish((baseTopic + "/sensors").c_str(), payload.c_str());
  }
  
  void loop() {
    if (!client->connected()) {
      connect();
    } else {
      client->loop();
    }
  }
};
```

## 物联网项目实战

### 智能环境监测系统

```cpp
#include <WiFi.h>
#include <WebServer.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <DHT.h>

// 传感器定义
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define LDR_PIN 34
#define PIR_PIN 2

// WiFi和MQTT配置
const char* ssid = "your_wifi_name";
const char* password = "your_wifi_password";
const char* mqtt_server = "broker.hivemq.com";

// 全局对象
DHT dht(DHT_PIN, DHT_TYPE);
WiFiClient espClient;
PubSubClient mqttClient(espClient);
WebServer webServer(80);
MQTTManager mqttManager(&mqttClient, "env_monitor_001");

// 传感器数据结构
struct SensorData {
  float temperature;
  float humidity;
  float light;
  bool motion;
  unsigned long timestamp;
};

// 配置参数
struct Config {
  int updateInterval = 5000;
  bool mqttEnabled = true;
  bool webServerEnabled = true;
  float tempThreshold = 25.0;
  float humidityThreshold = 70.0;
};

Config config;

void setup() {
  Serial.begin(115200);
  
  // 初始化传感器
  dht.begin();
  pinMode(PIR_PIN, INPUT);
  
  // 连接WiFi
  connectWiFi();
  
  // 设置MQTT
  mqttClient.setServer(mqtt_server, 1883);
  mqttClient.setCallback(mqttCallback);
  
  // 设置Web服务器
  setupWebServer();
  
  Serial.println("智能环境监测系统启动完成");
}

void loop() {
  // 处理MQTT
  if (config.mqttEnabled) {
    mqttManager.loop();
  }
  
  // 处理Web服务器
  if (config.webServerEnabled) {
    webServer.handleClient();
  }
  
  // 读取传感器数据
  static unsigned long lastUpdate = 0;
  if (millis() - lastUpdate >= config.updateInterval) {
    SensorData data = readSensors();
    processSensorData(data);
    lastUpdate = millis();
  }
  
  delay(100);
}

void connectWiFi() {
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("连接WiFi中...");
  }
  Serial.println("WiFi连接成功");
  Serial.print("IP地址: ");
  Serial.println(WiFi.localIP());
}

SensorData readSensors() {
  SensorData data;
  
  data.temperature = dht.readTemperature();
  data.humidity = dht.readHumidity();
  data.light = analogRead(LDR_PIN) * 100.0 / 4095.0; // 转换为百分比
  data.motion = digitalRead(PIR_PIN);
  data.timestamp = millis();
  
  return data;
}

void processSensorData(const SensorData& data) {
  // 打印到串口
  Serial.println("=== 传感器数据 ===");
  Serial.printf("温度: %.2f°C\n", data.temperature);
  Serial.printf("湿度: %.2f%%\n", data.humidity);
  Serial.printf("光照: %.1f%%\n", data.light);
  Serial.printf("运动: %s\n", data.motion ? "检测到" : "无");
  Serial.println("================");
  
  // 检查阈值
  if (data.temperature > config.tempThreshold) {
    Serial.println("警告:温度过高!");
    if (config.mqttEnabled) {
      mqttClient.publish("env_monitor_001/alerts", "高温警告");
    }
  }
  
  if (data.humidity > config.humidityThreshold) {
    Serial.println("警告:湿度过高!");
    if (config.mqttEnabled) {
      mqttClient.publish("env_monitor_001/alerts", "高湿警告");
    }
  }
  
  // 发布到MQTT
  if (config.mqttEnabled) {
    mqttManager.publishSensorData(data.temperature, data.humidity, data.light);
  }
}

void mqttCallback(char* topic, byte* payload, unsigned int length) {
  String message = "";
  for (int i = 0; i < length; i++) {
    message += (char)payload[i];
  }
  
  Serial.print("收到MQTT消息 [");
  Serial.print(topic);
  Serial.print("]: ");
  Serial.println(message);
  
  // 处理配置更新
  if (String(topic) == "devices/env_monitor_001/settings") {
    updateConfig(message);
  }
}

void updateConfig(String configJson) {
  StaticJsonDocument<200> doc;
  deserializeJson(doc, configJson);
  
  if (doc.containsKey("update_interval")) {
    config.updateInterval = doc["update_interval"];
  }
  if (doc.containsKey("temp_threshold")) {
    config.tempThreshold = doc["temp_threshold"];
  }
  if (doc.containsKey("humidity_threshold")) {
    config.humidityThreshold = doc["humidity_threshold"];
  }
  
  Serial.println("配置已更新");
}

void setupWebServer() {
  webServer.on("/", handleRoot);
  webServer.on("/api/data", handleApiData);
  webServer.on("/api/config", HTTP_GET, handleGetConfig);
  webServer.on("/api/config", HTTP_POST, handlePostConfig);
  
  webServer.begin();
  Serial.println("Web服务器启动");
}

void handleRoot() {
  String html = "<!DOCTYPE html><html><head><title>环境监测系统</title>";
  html += "<meta name='viewport' content='width=device-width, initial-scale=1'>";
  html += "<style>";
  html += "body { font-family: Arial, sans-serif; margin: 20px; }";
  html += ".card { background: #f0f0f0; padding: 20px; margin: 10px 0; border-radius: 5px; }";
  html += ".value { font-size: 24px; font-weight: bold; color: #333; }";
  html += "</style></head><body>";
  html += "<h1>智能环境监测系统</h1>";
  html += "<div class='card'>";
  html += "<h3>实时数据</h3>";
  html += "<p>温度: <span class='value' id='temp'>--</span>°C</p>";
  html += "<p>湿度: <span class='value' id='humidity'>--</span>%</p>";
  html += "<p>光照: <span class='value' id='light'>--</span>%</p>";
  html += "<p>运动: <span class='value' id='motion'>--</span></p>";
  html += "</div>";
  html += "<script>";
  html += "function updateData() {";
  html += "  fetch('/api/data')";
  html += "    .then(response => response.json())";
  html += "    .then(data => {";
  html += "      document.getElementById('temp').textContent = data.temperature.toFixed(1);";
  html += "      document.getElementById('humidity').textContent = data.humidity.toFixed(1);";
  html += "      document.getElementById('light').textContent = data.light.toFixed(1);";
  html += "      document.getElementById('motion').textContent = data.motion ? '检测到' : '无';";
  html += "    });";
  html += "}";
  html += "setInterval(updateData, 2000);";
  html += "updateData();";
  html += "</script></body></html>";
  
  webServer.send(200, "text/html", html);
}

void handleApiData() {
  SensorData data = readSensors();
  
  StaticJsonDocument<200> doc;
  doc["temperature"] = data.temperature;
  doc["humidity"] = data.humidity;
  doc["light"] = data.light;
  doc["motion"] = data.motion;
  doc["timestamp"] = data.timestamp;
  
  String response;
  serializeJson(doc, response);
  webServer.send(200, "application/json", response);
}

void handleGetConfig() {
  StaticJsonDocument<200> doc;
  doc["update_interval"] = config.updateInterval;
  doc["temp_threshold"] = config.tempThreshold;
  doc["humidity_threshold"] = config.humidityThreshold;
  doc["mqtt_enabled"] = config.mqttEnabled;
  doc["web_server_enabled"] = config.webServerEnabled;
  
  String response;
  serializeJson(doc, response);
  webServer.send(200, "application/json", response);
}

void handlePostConfig() {
  if (webServer.hasArg("plain")) {
    String configJson = webServer.arg("plain");
    updateConfig(configJson);
    webServer.send(200, "application/json", "{\"status\":\"success\"}");
  } else {
    webServer.send(400, "application/json", "{\"status\":\"error\"}");
  }
}
```

## 性能优化与最佳实践

### 电源管理

```cpp
#include "esp_sleep.h"

void enterDeepSleep(int seconds) {
  Serial.println("进入深度睡眠模式");
  esp_sleep_enable_timer_wakeup(seconds * 1000000);
  esp_deep_sleep_start();
}

void enterLightSleep() {
  Serial.println("进入轻度睡眠模式");
  esp_light_sleep_start();
}
```

### 内存管理

```cpp
void printMemoryInfo() {
  Serial.printf("可用堆内存: %d bytes\n", ESP.getFreeHeap());
  Serial.printf("最大分配块: %d bytes\n", ESP.getMaxAllocHeap());
  Serial.printf("最小可用堆: %d bytes\n", ESP.getMinFreeHeap());
}
```

### 错误处理与恢复

```cpp
void handleWiFiDisconnect() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi连接丢失,尝试重连...");
    WiFi.disconnect();
    WiFi.begin(ssid, password);
    
    int attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
      delay(1000);
      attempts++;
      Serial.print(".");
    }
    
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("WiFi重连成功");
    } else {
      Serial.println("WiFi重连失败,重启设备");
      ESP.restart();
    }
  }
}
```

## 总结

ESP32的WiFi功能为物联网项目提供了强大的基础。通过合理使用Web服务器、MQTT通信和传感器集成,可以构建出功能完整的智能设备。关键要点:

1. **稳定的网络连接**:实现自动重连和错误恢复机制
2. **高效的数据传输**:使用JSON格式和适当的压缩
3. **安全的通信**:考虑使用TLS加密和身份验证
4. **电源优化**:合理使用睡眠模式延长电池寿命
5. **用户体验**:提供直观的Web界面和实时数据更新

继续学习更多高级主题,如OTA更新、设备管理和云端集成,将帮助你构建更完善的物联网解决方案。