Day6.
机器人设计与应用综合实训——ESP32开发技术分享
本文为机器人设计与应用综合实训中,基于ESP32的开发技术分享帖,主要记录实训过程中的ESP32开发要点、问题排查及实践总结,适配实训报告补充、技术复盘及同学间交流使用。
核心方向:ESP32芯片基础应用、机器人控制模块开发、传感器数据交互、代码调试与优化,贴合实训核心需求,兼顾技术细节与实操记录。
|
实训项目名称 |
ESP32 WIFI联网与天气信息显示开发 |
实训时间 |
本次实训当日 |
ESP32开发板型号 |
ESP32-S3 |
开发环境 |
VSCode+ESP-IDF v5.4.2 |
实训小组人数 |
1人 |
二、ESP32开发核心知识点梳理
|
知识点类别 |
具体知识点 |
核心原理简述 |
实训应用场景<br/>(机器人相关) |
备注<br/>(易错点/重点) |
|
引脚配置 |
WIFI外设引脚复用 |
ESP32-S3内置WIFI模块,芯片已规划专用引脚实现WIFI射频通信,无需手动配置物理引脚,仅需软件使能 |
机器人联网通信、远程数据传输与控制 |
不可随意修改WIFI专用引脚配置,避免射频功能失效 |
|
外设驱动 |
WIFI STA模式驱动 |
STA模式下ESP32作为客户端连接无线路由器等AP设备,通过驱动程序实现网络扫描、连接、数据收发 |
机器人获取网络时间、访问网络天气接口 |
需正确配置WIFI账号密码,确保与AP的2.4G频段匹配 |
|
通信协议 |
HTTP客户端协议 |
基于TCP/IP协议栈,通过HTTP GET/POST请求向网络服务器发送数据并获取响应结果 |
机器人从天气服务器请求气象数据 |
需替换为自己的天气接口密钥,请求格式需匹配服务器要求 |
|
代码开发 |
NVS闪存初始化 |
NVS(非易失性存储)用于保存WIFI连接信息等配置,初始化后可实现数据掉电保存 |
WIFI连接信息持久化,避免重复配置 |
初始化失败时需执行擦除操作后重新初始化 |
|
代码开发 |
FreeRTOS任务调度与CPU释放 |
通过vTaskDelay实现延时,释放CPU资源,避免看门狗因主线程阻塞触发复位 |
WIFI联网、数据请求过程中的延时处理 |
网络操作处必须添加CPU释放操作,防止程序卡死 |
|
其他(自定义) |
网络时间同步 |
通过网络协议从时间服务器获取当前UTC时间,转换为本地时间 |
机器人时钟模块显示实时时间 |
同步时添加延时,避免网络卡顿导致程序异常 |
三、ESP32机器人开发实操步骤
|
步骤序号 |
实操内容<br/>(ESP32相关) |
操作步骤细节 |
使用工具/代码片段 |
操作结果<br/>(成功/失败及原因) |
|
1 |
开发环境搭建 |
确认VSCode中ESP-IDF v5.4.2配置正常,打开已有工程,检查工程结构完整性 |
VSCode、ESP-IDF插件 |
成功,工程可正常编译,无环境配置报错 |
|
2 |
ESP32开发板调试 |
连接ESP32-S3开发板与电脑Type-C口,确认设备管理器识别串口,测试开发板供电与复位功能 |
Type-C数据线、电脑设备管理器 |
成功,串口识别正常,开发板复位无异常 |
|
3 |
外设与ESP32连接 |
无需物理外设连接,仅在工程中添加WIFI组件文件夹,包含wifi.c和wifi.h驱动文件 |
文件资源管理器 |
成功,WIFI组件文件添加至工程指定目录 |
|
4 |
代码编写与烧录 |
1. 在CMakeLists.txt中添加WIFI头文件访问路径;<br/>2. 在main.c中引入wifi.h,调用Wifi_STA_Init()初始化WIFI;<br/>3. 修改wifi.c中WIFI账号密码(电脑共享2.4G热点);<br/>4. 替换天气接口密钥,解除http_post_request()注释;<br/>5. 添加网络时间初始化ESP_Network_Init(),编写时间显示代码;<br/>6. 编译并通过UART方式烧录程序 |
#include "wifi.h"<br/>Wifi_STA_Init();<br/>ESP_Network_Init();<br/>ESP-IDF编译、烧录按钮 |
成功,程序无编译错误,烧录过程无失败提示 |
|
5 |
机器人功能调试 |
1. 开启电脑2.4G共享热点,确保开发板联网;<br/>2. 打开ESP-IDF监视器,查看WIFI连接日志;<br/>3. 检查LCD屏是否显示实时网络时间;<br/>4. 验证天气数据是否正常请求并解析 |
ESP-IDF监视器、LCD显示屏 |
成功,WIFI正常连接热点,LCD实时显示时间,天气信息可正常获取 |
|
6 |
功能优化与完善 |
在WIFI连接、网络时间同步、天气请求代码处添加vTaskDelay(10)释放CPU资源;关闭无用的天气数据冗余获取代码 |
vTaskDelay(10); |
成功,程序运行稳定,无看门狗复位、卡死现象 |
四、ESP32开发常见问题及解决方案
|
问题序号 |
问题描述(ESP32相关) |
排查过程 |
解决方案 |
问题总结(避免方法) |
|
1 |
WIFI初始化失败,提示NVS相关错误 |
查看监视器报错信息,定位为nvs_flash_init()返回错误码 |
在Wifi_STA_Init()中添加NVS擦除逻辑:<br/>if (ret== ESP_ERR_NVS_NO_FREE_PAGES |
|
|
2 |
烧录后开发板无法连接WIFI,提示连接超时 |
1. 检查热点频段是否为2.4G(ESP32-S3部分版本不支持5G);<br/>2. 核对wifi.c中账号密码是否与热点一致 |
1. 将电脑共享热点切换为2.4G频段;<br/>2. 准确修改WIFI账号密码,无空格、大小写错误 |
确保WIFI热点为2.4G,配置信息与热点完全一致,避免因频段、密码问题导致连接失败 |
|
3 |
程序运行中出现看门狗复位,串口提示任务阻塞 |
排查代码,发现网络时间同步、WIFI数据请求处无CPU释放操作,主线程长期占用CPU |
在while循环和网络操作相关代码处添加vTaskDelay(10),定期释放CPU资源 |
所有耗时操作(网络、外设通信)处必须添加延时释放CPU,避免触发看门狗机制 |
|
4 |
天气数据无法获取,提示请求失败 |
检查天气接口密钥是否过期,http_post_request()函数是否被注释 |
1. 替换为自己的有效天气接口密钥;<br/>2. 解除wifi.c中http_post_request()的注释,确保请求函数正常调用 |
使用专属的接口密钥,确认功能函数未被注释,保证网络请求代码正常执行 |
|
5 |
LCD屏无时间显示,程序无其他报错 |
检查main.c中是否调用ESP_Network_Init(),时间解析与显示代码是否完整 |
1. 添加网络时间初始化代码ESP_Network_Init();<br/>2. 完善时间解析代码:timeinfo = *localtime(timer_sec);,确保LCD标签赋值正常 |
确认功能初始化代码完整,数据解析与外设显示的代码逻辑无缺失 |
五、实训总结与ESP32开发心得
|
项目完成情况 |
本次实训目标为实现ESP32-S3的WIFI联网、网络时间同步与天气信息显示,所有功能均成功实现,程序运行稳定,LCD屏可实时显示时间,能正常从网络接口获取天气数据,无卡顿、复位等异常现象 |
|
ESP32开发重点收获 |
1. 掌握了ESP32-S3 WIFI STA模式的开发流程,包括驱动移植、配置与初始化;<br/>2. 理解了NVS闪存的作用与初始化方法,解决了NVS相关的初始化异常问题;<br/>3. 学会了基于ESP-IDF的HTTP客户端开发,能通过网络请求获取服务器数据;<br/>4. 掌握了FreeRTOS中CPU资源释放的技巧,避免看门狗复位问题;<br/>5. 理解了工程中CMakeLists.txt的配置规则,学会添加头文件路径与源文件;<br/>6. 掌握了网络时间同步的实现方法,能将网络时间解析并显示在LCD屏 |
|
存在的不足 |
1. 对HTTP协议的细节理解不够深入,仅能调用现成函数实现请求,无法自主编写复杂的网络请求代码;<br/>2. 对ESP32-S3的WIFI底层驱动原理了解较少,仅能完成上层应用开发;<br/>3. 天气数据仅实现了基础获取,未进行数据解析与格式化显示,功能较为基础;<br/>4. 程序的异常处理不够完善,未考虑热点断开、网络卡顿等极端情况的应对 |
|
后续改进计划 |
1. 学习HTTP协议的详细原理,尝试自主编写GET/POST请求代码,实现更灵活的网络通信;<br/>2. 深入学习ESP32 WIFI底层驱动,了解射频通信、网络协议栈的相关知识;<br/>3. 完善天气数据的解析与格式化,将温度、湿度、天气状况等信息分区域显示在LCD屏;<br/>4. 添加网络异常处理代码,实现WIFI断连重连、请求超时提示等功能;<br/>5. 尝试将WIFI功能与机器人其他模块结合,实现远程控制、数据上传等更复杂的功能 |
1. CMakeLists.txt添加WIFI头文件路径关键代码
file(GLOB_RECURSE SOURCES ./*.c hello_world_main.c)
set(INCLUDE_DIRS "."
"LCD"
"Timer"
"batch"
"ui/generated"
"ui/custom"
"ui/generated/guider_customer_fonts"
"WIFI"
)
idf_component_register(SRCS ${SOURCES}
INCLUDE_DIRS ${INCLUDE_DIRS}
)
2. main.c中WIFI与网络时间初始化关键代码
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "LCD/LCD.h"
#include "tig.h"
#include "Timer.h"
#include "demos/lv_demos.h"
#include "gui_guider.h"
#include "custom.h"
#include "wifi.h"
lv_ui guider_ui;
void app_main(void)
{
bsp_i2c_init(); // IIC接口初始化
pca9557_init(); // 扩展口初始化
// bsp_lcd_init(); // LCD初始化
ESP_Timer_Init(); // 定时器初始化
bsp_lvgl_start(); // LVGL初始化
Wifi_STA_Init(); // WIFI初始化
ESP_Network_Init();
setup_ui(&guider_ui);
custom_init(&guider_ui);
uint32_t sec = timer_sec;
while(1){
if(sec != timer_sec){ // 每秒进入一次
sec = timer_sec;
char str[50]={0};
sprintf(str,"%02d:%02d:%02d",timeinfo.tm_hour,timeinfo.tm_min,timeinfo.tm_sec);
lv_label_set_text(guider_ui.screen_label_1, str);
printf("天气: %s\n",weather[0].rain);
printf("温度: %s\n",weather[0].temp);
}
vTaskDelay(10); // 释放CPU资源1ms
}
}
3. wifi.c中NVS初始化与天气请求关键代码
#include <stdio.h>
#include "wifi.h"
EventGroupHandle_t wifi_event_group = NULL;
static uint8_t WiFi_retry_num = 0;
// 今天~后天的数据
WEATHER_Type weather[3];
static bool gl_get_finish = false;
char send_buf[256] = {0};
/**
* wifi事件回调函数
*/
void wifi_event_handler (void* event_handler_arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{
if (event_base == WIFI_EVENT){ //WIFI事件
switch (event_id){
case WIFI_EVENT_STA_START://WIFI开始连接
ESP_LOGI(TAG,"WIFI正在连接");
esp_wifi_connect();//重新连接
break;
case WIFI_EVENT_STA_CONNECTED://WIFI已连接
ESP_LOGI(TAG,"WIFI连接成功");
break;
case WIFI_EVENT_STA_DISCONNECTED://WIFI已断开
vTaskDelay(2000 / portTICK_PERIOD_MS);
ESP_LOGI(TAG,"WIFI正在连接");
esp_wifi_connect();//重新连接
break;
default:break;
}
}
if (event_base == IP_EVENT){ //IP事件
switch (event_id){
case IP_EVENT_STA_GOT_IP:
ip_event_got_ip_t *ip_event = (ip_event_got_ip_t *)event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&ip_event->ip_info.ip));
xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT);//设置WIFI已连接
break;
}
}
}
void WifiConfig(void)
{
wifi_event_group = xEventGroupCreate();//创建事件组
esp_netif_init();//初始化网络
esp_event_loop_create_default();//创建默认事件组
esp_netif_t *interface = esp_netif_create_default_wifi_sta();//创建默认WIFI STA
esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL);//注册WIFI事件
esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL); //注册IP事件
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//初始化WIFI
ESP_ERROR_CHECK(esp_wifi_init(&cfg));//初始化WIFI
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));//设置WIFI模式为STA
uint8_t mac[8];
esp_efuse_mac_get_default(mac);
char hostname[32] = {0};
sprintf(hostname, "ESP32-S3-%02X%02X", mac[4], mac[5]);
esp_netif_set_hostname(interface, hostname);
wifi_config_t wifi_config = {
.sta = {
.ssid = WiFi_STA_SSID,
.password = WiFi_STA_PASSWORD,
.bssid_set = 0,
}};
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "wifi_init_sta finished.");
ESP_LOGI(TAG, "wifi start connect to AP.");
while (true){ //等待WIFI连接成功
EventBits_t bits = xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdTRUE, pdFALSE, portMAX_DELAY);
if (bits & WIFI_CONNECTED_BIT){
break;
}
}
ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",WiFi_STA_SSID, WiFi_STA_PASSWORD);
}
// JSON解析函数
static void cJSON_parse_task(char *text)
{
char *date, *Temp_High = NULL, *Temp_Low, *humidity, *Rainfall;
cJSON *root, *arrayItem, *subArray;
cJSON *arr_item, *sub_array_item;
cJSON *JsonDate, *JsonTemp_High, *JsonTemp_Low, *JsonHumidity, *JsonText_day;
root = cJSON_Parse(text);
if (root != NULL){
arrayItem = cJSON_GetObjectItem(root, "results"); //获取数组大小
int arr_size = cJSON_GetArraySize(arrayItem); //获取数组大小
ESP_LOGI(HTTP_TAG, "root_arr_size: %d \n", arr_size);
cJSON *first_result = cJSON_GetArrayItem(arrayItem, 0);
cJSON *location = cJSON_GetObjectItem(first_result, "location");
cJSON *name = cJSON_GetObjectItem(location, "name");
if (cJSON_IsString(name)){
ESP_LOGI(RESULT_TAG, "%s ", name->valuestring);
}
arr_item = arrayItem->child;//获取数组第一个元素
for (int i = 0; i < arr_size; i++){
subArray = cJSON_GetObjectItem(arr_item, "daily");
int sub_array_size = cJSON_GetArraySize(subArray);
sub_array_item = subArray->child;
ESP_LOGI(HTTP_TAG, "sub_arr_size: %d \n", sub_array_size);
for (int j = 0; j < sub_array_size; j++){
printf("\t %d组数据解析开始 \n", j);
// ESP_LOGE(HTTP_TAG, "Data IS json");
if (sub_array_item->type == cJSON_Object){
JsonDate = cJSON_GetObjectItem(sub_array_item, "date");
if (cJSON_IsString(JsonDate)){
date = JsonDate->valuestring;//获取时间
sprintf(weather[j].date, " %s ", date);
ESP_LOGI(RESULT_TAG, "%s ", weather[j].date);
}
JsonTemp_High = cJSON_GetObjectItem(sub_array_item, "high");
if (cJSON_IsString(JsonTemp_High))
Temp_High = JsonTemp_High->valuestring;
JsonTemp_Low = cJSON_GetObjectItem(sub_array_item, "low");
if (cJSON_IsString(JsonTemp_Low)){
Temp_Low = JsonTemp_Low->valuestring;
sprintf(weather[j].temp, " %s-%s ", Temp_Low, Temp_High);
ESP_LOGI(RESULT_TAG, "低-高温度:%s", weather[j].temp);
}
JsonHumidity = cJSON_GetObjectItem(sub_array_item, "humidity");
if (cJSON_IsString(JsonHumidity)){
humidity = JsonHumidity->valuestring;
sprintf(weather[j].humi, " %s ", humidity);
ESP_LOGI(RESULT_TAG, "湿度:%s", weather[j].humi);
}
JsonText_day = cJSON_GetObjectItem(sub_array_item, "text_day");
if (cJSON_IsString(JsonText_day)){
Rainfall = JsonText_day->valuestring;
sprintf(weather[j].rain, " %s ", Rainfall);
ESP_LOGI(RESULT_TAG, "%s", weather[j].rain);
}
JsonText_day = cJSON_GetObjectItem(sub_array_item, "code_day");
if (cJSON_IsString(JsonText_day)){
Rainfall = JsonText_day->valuestring;
sscanf(Rainfall, "%d", &weather[j].code);
ESP_LOGI(RESULT_TAG, "%d", weather[j].code);
}
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
sub_array_item = sub_array_item->next;
}
arr_item = arr_item->next;
}
printf("\n");
ESP_LOGI(HTTP_TAG, "Finish");
gl_get_finish = true;
}
cJSON_Delete(root);
}
static void http_post_request(void)
{
char output_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0}; // Buffer to store response of http request
int content_length = 0;
static const char *URL = "http://" HOST "/v3/weather/daily.json?"
"key=" UserKey "&location=" Location
"&language=" Language
"&unit=c&start=" Strat "&days=" Days;
/*https://api.seniverse.com/v3/weather/daily.json?key=So18Lkv4F4hn2d3aT&location=nanning&language=zh-Hans&unit=c&start=0&days=5*/
esp_http_client_config_t config = {
.url = URL,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// GET Request
esp_http_client_set_method(client, HTTP_METHOD_GET);
esp_err_t err = esp_http_client_open(client, 0);// 打开HTTP连接
if (err != ESP_OK){
ESP_LOGE(HTTP_TAG, "Failed to open HTTP connection: %s", esp_err_to_name(err));
}else{
content_length = esp_http_client_fetch_headers(client);
if (content_length < 0){
ESP_LOGE(HTTP_TAG, "HTTP client fetch headers failed");
}else{
int data_read = esp_http_client_read_response(client, output_buffer, MAX_HTTP_OUTPUT_BUFFER);
if (data_read >= 0){
ESP_LOGI(HTTP_TAG, "HTTP GET Status = %d, content_length = %lld",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
// 打印天气信息
ESP_LOGI(WiFi_TAG, "\n\n\n %s \n", output_buffer);
// JSON解析
cJSON_parse_task(output_buffer);
}else{
ESP_LOGE(HTTP_TAG, "Failed to read response");
}
}
}
esp_http_client_close(client);
}
void Wifi_STA_Init(void)
{
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
WifiConfig();
http_post_request(); // 请求天气数据
}
static void http_post_GLM(void)
{
char output_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
esp_http_client_config_t config = {
.url = "https://open.bigmodel.cn/api/paas/v4/chat/completions",
// .event_handler = http_event_handler, // 添加事件处理
// .timeout_ms = 80000, // 增加超时到80秒
// .skip_cert_common_name_check = true, // 跳过证书CN检查
// .auth_type = HTTP_AUTH_TYPE_NONE,
// .cert_pem = NULL, // 不指定证书
};
esp_http_client_handle_t client = esp_http_client_init(&config);
const char *json_data =
"{"
"\"model\":\"GLM-4-Flash-250414\","
"\"messages\":["
"{"
"\"role\": \"user\","
"\"content\": \"你好\"" // 填入问题内容
"}"
"]"
"}";
esp_http_client_set_method(client, HTTP_METHOD_POST);
esp_http_client_set_header(client, "Content-Type", "application/json");
esp_http_client_set_header(client, "Authorization", "76b5d293386f4a88b7d44e0cbc5f97fd.jCCXsqlgsHvJ9NyO");//key_API
esp_err_t err = esp_http_client_open(client, strlen(json_data));
if (err != ESP_OK) {
ESP_LOGE(TAG, "连接失败: %s", esp_err_to_name(err));
esp_http_client_cleanup(client);
esp_http_client_close(client);
}
int wlen = esp_http_client_write(client, json_data, strlen(json_data));
if (wlen < 0) {
ESP_LOGE(TAG, "数据写入失败");
esp_http_client_cleanup(client);
esp_http_client_close(client);
}
int content_length = esp_http_client_fetch_headers(client);
if (content_length < 0) {
ESP_LOGE(TAG, "获取头部信息失败");
esp_http_client_cleanup(client);
esp_http_client_close(client);
}
int data_read = esp_http_client_read_response(client, output_buffer, MAX_HTTP_OUTPUT_BUFFER);
if (data_read >= 0) {
ESP_LOGI(TAG, "HTTP状态码 = %d, 内容长度 = %"PRIu64,
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
ESP_LOGI(TAG, "响应内容: %s", output_buffer);
} else {
ESP_LOGE(TAG, "读取响应失败");
}
esp_http_client_cleanup(client);
esp_http_client_close(client);
}
static void http_task(void *pvParameters)
{
http_post_GLM();
ESP_LOGI(TAG, "HTTP示例完成");
vTaskDelete(NULL);
}
void Wifi_GLM_Init(void)
{
// 初始化NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK( ret );
WifiConfig();
// 创建HTTP任务
xTaskCreate(&http_task, "http_task", 8192, NULL, 5, NULL);
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)