Appearance
第十一章:综合项目实战
项目:IoT 环境监测节点
功能概述
硬件:ESP32-S3-N16R8 开发板
+ DHT22 温湿度传感器(GPIO4)
+ SSD1306 OLED 显示屏(I2C,GPIO21/22)
+ 状态 LED(GPIO2)
+ 按键(GPIO0)
功能:
1. 每 5 秒采集温湿度数据
2. OLED 实时显示数据
3. 通过 Wi-Fi 上报到 MQTT Broker
4. 支持 BLE 配网(首次使用)
5. 数据存储到 SPIFFS(断网时本地缓存)
6. 按键切换 OLED 显示页面
7. 支持 OTA 固件升级系统架构
┌─────────────────────────────────────────────────────┐
│ app_main │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ sensor_task display_task network_task │
│ (Core 0) (Core 0) (Core 1) │
│ │ │ │ │
│ └───────────────┴─────────────┘ │
│ │ │
│ sensor_data_queue │
│ (任务间通信) │
└─────────────────────────────────────────────────────┘11.1 项目结构
iot_monitor/
├── CMakeLists.txt
├── partitions.csv # 自定义分区表
├── sdkconfig.defaults # 默认配置
├── main/
│ ├── CMakeLists.txt
│ ├── main.c # 入口,初始化和任务创建
│ ├── app_config.h # 全局配置宏
│ └── app_config.c # 配置管理(NVS)
├── components/
│ ├── sensor/
│ │ ├── sensor.h
│ │ └── sensor.c # DHT22 驱动
│ ├── display/
│ │ ├── display.h
│ │ └── display.c # OLED 显示
│ ├── network/
│ │ ├── wifi_manager.h
│ │ ├── wifi_manager.c # Wi-Fi 连接管理
│ │ ├── mqtt_client.h
│ │ └── mqtt_client.c # MQTT 上报
│ └── ble_prov/
│ ├── ble_prov.h
│ └── ble_prov.c # BLE 配网11.2 核心代码
app_config.h
c
#pragma once
// 版本信息
#define APP_VERSION "1.0.0"
#define APP_NAME "IoT Monitor"
// 硬件引脚
#define PIN_DHT22 GPIO_NUM_4
#define PIN_LED_STATUS GPIO_NUM_2
#define PIN_BTN_MODE GPIO_NUM_0
#define PIN_OLED_SDA GPIO_NUM_21
#define PIN_OLED_SCL GPIO_NUM_22
// 采集间隔
#define SENSOR_INTERVAL_MS 5000
// MQTT
#define MQTT_BROKER_URI "mqtt://broker.emqx.io:1883"
#define MQTT_TOPIC_DATA "iot/monitor/%s/data"
#define MQTT_TOPIC_CMD "iot/monitor/%s/cmd"
// NVS 命名空间
#define NVS_NS_CONFIG "app_cfg"
#define NVS_KEY_WIFI_SSID "wifi_ssid"
#define NVS_KEY_WIFI_PASS "wifi_pass"
#define NVS_KEY_DEVICE_ID "device_id"
#define NVS_KEY_BOOT_CNT "boot_cnt"
// 事件位
#define EVT_WIFI_READY BIT0
#define EVT_MQTT_READY BIT1
#define EVT_SENSOR_OK BIT2
#define EVT_BLE_PROV_DONE BIT3main.c
c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_mac.h"
#include "app_config.h"
#include "sensor.h"
#include "display.h"
#include "wifi_manager.h"
#include "mqtt_client_app.h"
static const char *TAG = "MAIN";
// 全局句柄
QueueHandle_t g_sensor_queue;
EventGroupHandle_t g_app_events;
char g_device_id[16];
// 传感器数据结构
typedef struct {
float temperature;
float humidity;
int64_t timestamp_ms;
bool valid;
} sensor_data_t;
// 传感器采集任务
static void sensor_task(void *arg)
{
sensor_data_t data;
ESP_LOGI(TAG, "传感器任务启动");
while (1) {
data.timestamp_ms = esp_timer_get_time() / 1000;
data.valid = sensor_read(&data.temperature, &data.humidity);
if (data.valid) {
ESP_LOGI(TAG, "温度: %.1f°C 湿度: %.1f%%",
data.temperature, data.humidity);
xQueueOverwrite(g_sensor_queue, &data); // 覆盖旧数据
xEventGroupSetBits(g_app_events, EVT_SENSOR_OK);
} else {
ESP_LOGW(TAG, "传感器读取失败");
}
vTaskDelay(pdMS_TO_TICKS(SENSOR_INTERVAL_MS));
}
}
// 显示任务
static void display_task(void *arg)
{
sensor_data_t data = {0};
uint8_t page = 0;
uint32_t boot_count = 0;
nvs_read_int(NVS_NS_CONFIG, NVS_KEY_BOOT_CNT,
(int32_t *)&boot_count, 0);
display_init();
display_show_boot(APP_NAME, APP_VERSION, boot_count);
vTaskDelay(pdMS_TO_TICKS(2000));
while (1) {
// 非阻塞读取最新传感器数据
xQueuePeek(g_sensor_queue, &data, 0);
switch (page) {
case 0:
display_show_sensor(data.temperature, data.humidity);
break;
case 1:
display_show_network(wifi_get_ip(), wifi_is_connected());
break;
case 2:
display_show_system(esp_get_free_heap_size(),
esp_timer_get_time() / 1000000);
break;
}
// 检测按键切换页面
if (button_pressed()) {
page = (page + 1) % 3;
}
vTaskDelay(pdMS_TO_TICKS(200));
}
}
// 网络任务
static void network_task(void *arg)
{
sensor_data_t data;
char topic[64];
char payload[128];
ESP_LOGI(TAG, "网络任务启动,等待 Wi-Fi...");
// 等待 Wi-Fi 连接
xEventGroupWaitBits(g_app_events, EVT_WIFI_READY,
pdFALSE, pdTRUE, portMAX_DELAY);
// 初始化 MQTT
mqtt_init(MQTT_BROKER_URI, g_device_id);
xEventGroupWaitBits(g_app_events, EVT_MQTT_READY,
pdFALSE, pdTRUE, pdMS_TO_TICKS(10000));
snprintf(topic, sizeof(topic), MQTT_TOPIC_DATA, g_device_id);
while (1) {
// 等待新的传感器数据
if (xQueueReceive(g_sensor_queue, &data, pdMS_TO_TICKS(10000))) {
if (data.valid && mqtt_is_connected()) {
snprintf(payload, sizeof(payload),
"{\"device\":\"%s\",\"temp\":%.1f,"
"\"humi\":%.1f,\"ts\":%lld}",
g_device_id, data.temperature,
data.humidity, data.timestamp_ms);
mqtt_publish(topic, payload, 1);
ESP_LOGI(TAG, "数据已上报: %s", payload);
}
}
}
}
void app_main(void)
{
// 1. 基础初始化
ESP_ERROR_CHECK(nvs_flash_init());
// 2. 生成设备 ID(基于 MAC 地址)
uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA);
snprintf(g_device_id, sizeof(g_device_id),
"%02X%02X%02X%02X", mac[2], mac[3], mac[4], mac[5]);
ESP_LOGI(TAG, "设备 ID: %s", g_device_id);
// 3. 更新开机计数
int32_t boot_count = 0;
nvs_read_int(NVS_NS_CONFIG, NVS_KEY_BOOT_CNT, &boot_count, 0);
boot_count++;
nvs_write_int(NVS_NS_CONFIG, NVS_KEY_BOOT_CNT, boot_count);
ESP_LOGI(TAG, "第 %"PRId32" 次开机", boot_count);
// 4. 创建全局对象
g_sensor_queue = xQueueCreate(1, sizeof(sensor_data_t));
g_app_events = xEventGroupCreate();
// 5. 初始化传感器
sensor_init(PIN_DHT22);
// 6. 检查 Wi-Fi 配置
char ssid[32] = {0}, pass[64] = {0};
nvs_read_str(NVS_NS_CONFIG, NVS_KEY_WIFI_SSID, ssid, sizeof(ssid));
if (strlen(ssid) == 0) {
// 无配置,启动 BLE 配网
ESP_LOGI(TAG, "无 Wi-Fi 配置,启动 BLE 配网...");
ble_prov_start(g_device_id, g_app_events);
xEventGroupWaitBits(g_app_events, EVT_BLE_PROV_DONE,
pdFALSE, pdTRUE, portMAX_DELAY);
nvs_read_str(NVS_NS_CONFIG, NVS_KEY_WIFI_SSID, ssid, sizeof(ssid));
nvs_read_str(NVS_NS_CONFIG, NVS_KEY_WIFI_PASS, pass, sizeof(pass));
} else {
nvs_read_str(NVS_NS_CONFIG, NVS_KEY_WIFI_PASS, pass, sizeof(pass));
}
// 7. 连接 Wi-Fi
wifi_init();
if (wifi_connect(ssid, pass) == ESP_OK) {
xEventGroupSetBits(g_app_events, EVT_WIFI_READY);
}
// 8. 创建任务
xTaskCreatePinnedToCore(sensor_task, "sensor", 4096, NULL, 10, NULL, 0);
xTaskCreatePinnedToCore(display_task, "display", 4096, NULL, 5, NULL, 0);
xTaskCreatePinnedToCore(network_task, "network", 8192, NULL, 8, NULL, 1);
ESP_LOGI(TAG, "系统初始化完成,所有任务已启动");
}11.3 项目扩展方向
扩展1:数据可视化
ESP32-S3 → MQTT → Node-RED → Grafana 仪表盘
→ 手机 App(MQTT Dashboard)扩展2:低功耗设计
c
// 深度睡眠(Deep Sleep):最低功耗
#include "esp_sleep.h"
// 配置唤醒源(定时器唤醒)
esp_sleep_enable_timer_wakeup(60 * 1000000ULL); // 60 秒
// 保存数据到 RTC 内存(深度睡眠后保留)
RTC_DATA_ATTR int boot_count = 0;
// 进入深度睡眠
esp_deep_sleep_start();
// 唤醒后从 app_main 重新开始扩展3:本地 Web 界面
ESP32-S3 运行 HTTP 服务器
手机浏览器访问 http://192.168.x.x
→ 实时数据图表(WebSocket 推送)
→ 配置页面
→ 历史数据查询📝 综合项目练习
项目 A:智能温控器(入门级)
功能:
- 读取温度传感器
- 设定目标温度(按键调节)
- 温度超过阈值:继电器控制风扇/加热器
- OLED 显示当前温度和目标温度
- 通过 UART 命令修改参数
涉及知识:GPIO + ADC/I2C + UART + NVS
项目 B:Wi-Fi 时钟(中级)
功能:
- 连接 Wi-Fi 后通过 NTP 同步时间
- OLED 显示时间、日期、星期
- 可设置闹钟(NVS 保存)
- 闹钟触发时 LED 闪烁 + 蜂鸣器响
涉及知识:Wi-Fi + SNTP + OLED + NVS + PWM
项目 C:BLE 遥控小车(中级)
功能:
- 手机 App 通过 BLE 发送方向命令
- ESP32-S3 控制 4 个电机(L298N 驱动)
- PWM 控制速度
- 超声波传感器避障
- 电池电量监测(ADC)
涉及知识:BLE + GPIO + PWM + ADC + I2C
项目 D:IoT 数据采集平台(高级)
功能:
- 多传感器采集(温湿度、光照、CO2)
- Wi-Fi 上报到云平台(阿里云 IoT / AWS IoT)
- 本地 SPIFFS 缓存(断网时存储,联网后补传)
- OTA 远程升级
- BLE 配网
- Web 配置界面
涉及知识:全部章节综合
学习路线回顾
第1章 环境搭建 → 能编译烧录程序
第2章 GPIO → 能控制 LED 和读取按键
第3章 中断/定时器 → 能响应实时事件
第4章 UART → 能与外部设备串口通信
第5章 I2C/SPI → 能驱动传感器和显示屏
第6章 ADC → 能采集模拟信号
第7章 FreeRTOS → 能编写多任务程序
第8章 存储 → 能持久化保存数据
第9章 Wi-Fi → 能联网上报数据
第10章 BLE → 能与手机蓝牙通信
第11章 综合项目 → 能完成完整 IoT 产品推荐学习资源
| 资源 | 链接 |
|---|---|
| ESP-IDF 官方文档 | https://docs.espressif.com/projects/esp-idf/zh_CN/ |
| ESP32-S3 技术参考手册 | https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_cn.pdf |
| ESP-IDF GitHub | https://github.com/espressif/esp-idf |
| 乐鑫官方示例 | https://github.com/espressif/esp-idf/tree/master/examples |
| FreeRTOS 文档 | https://www.freertos.org/Documentation/RTOS_book.html |
| nRF Connect(BLE 调试) | 手机应用商店搜索 |
| MQTT Explorer | https://mqtt-explorer.com/ |