ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ESP32联网实战:ESP-IDF实现WiFi获取天气与DHT11温湿度

ESP32联网实战:ESP-IDF实现WiFi获取天气与DHT11温湿度 做嵌入式这几年我一直觉得ESP32最让人上头的一点就是——它不只是个单片机而是一块自带WiFi和蓝牙的“小开发板电脑”。很多朋友在Arduino上玩过ESP8266、ESP32一旦切换到ESP-IDF第一道坎就是WiFi连接和网络请求怎么写。这节系列教程我就拿一个非常典型的场景来拆ESP32通过WiFi获取温度和天气信息。一方面用DHT11读本地温度湿度另一方面从网络API拉取城市天气数据两条线汇合到串口日志里把从硬件采集到云端数据解析的整条链路完整走通。这篇适合已经搭好ESP-IDF开发环境、想进阶到联网项目的开发者。1. 项目需求拆解本地传感器温度和城市天气信息如何合并1.1 标题背后的真实需求这个项目标题看起来简单——“ESP32获取温度和天气信息”但仔细一拆你会发现它其实包含了两组完全不同的数据流本地温度通过DHT11/DS18B20这类温湿度传感器读取当前环境的温度、湿度数据源在板卡本地天气信息通过WiFi联网请求第三方天气API获取某个城市当前的温度、天气现象、风力风向数据源在云端。这两组数据放在一起正好构成了一个典型的物联网数据终端本地采集 云端同步。很多智能家居项目、环境监测站、桌面天气摆件本质上都是这个架构。区别只在于把数据展示在OLED屏、手机App还是网页上。标题里的“05-2”也透露出这是系列教程的一个递进节点前面大概率已经讲了GPIO、定时器、I2C之类的外设操作这一节开始往“联网”走。所以你需要的不是一个单独点亮的Demo而是一套能继续往上扩展的基础框架——这也是我为什么要把WiFi连接、HTTP请求、JSON解析这些底层模块单独抽出来讲的原因。1.2 方案选型为什么选HTTPJSON而不是MQTT第一次做这类项目时很容易在通信协议上纠结用HTTP去轮询天气还是用MQTT去订阅我的建议很直接——纯天气查询场景HTTP GET请求 JSON解析是成本最低、最不容易出错的选择。原因有三点天气API基本都是RESTful风格一个GET请求就能拿到完整数据响应是标准的JSON格式没有多余的学习成本MQTT适合设备端和服务器之间需要双向实时通信的场景而天气数据是远程服务器主动提供、设备被动获取的MQTT的优势体现不出来ESP-IDF原生提供了esp_http_client组件和cJSON库接口封装得很好不用引入额外依赖。当然如果你的目标是做“设备上报温湿度到云平台再从云平台下发指令”那MQTT就是更优解这个后续系列可以展开。这次先把HTTP这条链路吃透。2. 硬件连接与IDF工程准备2.1 硬件清单与接线硬件部分我选了最稳妥的一套组合都是手边容易买到的器件型号/规格用途主控ESP32-DevKitCESP-WROOM-32运行ESP-IDF程序温湿度传感器DHT11或DHT22读取本地温湿度电阻4.7kΩ ~ 10kΩ上拉电阻DHT11数据线上拉面包板杜邦线若干连接电路接线其实很简单DHT11一共三个引脚有些模块是四个脚其中一个是空脚按下面接就行VCC → 3.3VDHT11可以3.3V供电但注意线长别太长GND → GNDDATA → GPIO4这个引脚可以自己改代码里对应调整即可在DATA和VCC之间接一个4.7kΩ上拉电阻这里说下为什么需要上拉电阻。DHT11用的是单总线协议总线空闲状态下是高电平主机和设备通过拉低总线来发起通信。如果没有外部上拉信号线会因为寄生电容和各种噪声导致电平不稳定最典型的现象就是读取到的数据忽大忽小、偶尔全是0xFF。这个电阻不是可选项是必选项。2.2 创建IDF工程与menuconfig配置我的开发环境是ESP-IDF v5.2在Ubuntu下用idf.py命令行工具管理工程。新版本的IDF都支持直接创建工程目录idf.py create-project esp32_weather cd esp32_weather工程创建好后需要用menuconfig配置WiFi信息。IDF的WiFi示例框架里通常会使用“Example Connection Configuration”这套Kconfig配置我建议你沿用这个惯例这样代码可读性更好换网络时也不用改源码重新编译。在工程根目录新建一个Kconfig.projbuild文件menu Example Connection Configuration config EXAMPLE_WIFI_SSID string WiFi SSID default myssid config EXAMPLE_WIFI_PASSWORD string WiFi Password default mypassword endmenu然后运行idf.py menuconfig在“Example Connection Configuration”菜单里填入你自己的WiFi账号和密码。这样程序里就能通过CONFIG_EXAMPLE_WIFI_SSID和CONFIG_EXAMPLE_WIFI_PASSWORD拿到配置项换环境时不用动代码。注意如果路由器开了5GHz频段ESP32的WiFi是连不上的。2.4GHz是标配。调试阶段建议让开发板离路由器近一点排除信号强度干扰。3. WiFi连接与HTTP请求的代码实现3.1 用事件组管理WiFi连接状态ESP-IDF的WiFi编程模型和Arduino差异很大。Arduino的WiFi.begin()是阻塞式的连接成功返回1失败返回0。IDF里WiFi是基于事件驱动的——你调用esp_wifi_start()之后系统在后台异步连接你通过事件处理器event handler来感知连接状态。新手最容易栽的地方就是调用了esp_wifi_connect()后立刻去发HTTP请求结果失败。因为此时WiFi还没连上更别说获取到IP地址了。我习惯用**事件组Event Group**来同步连接状态。首先定义几个事件位#define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 static EventGroupHandle_t s_wifi_event_group;然后在事件处理器里根据事件类型设置对应的位static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_DISCONNECTED) { esp_wifi_connect(); ESP_LOGI(TAG, retry connecting to AP...); } else if (event_base IP_EVENT event_id IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t *event (ip_event_got_ip_t *)event_data; ESP_LOGI(TAG, got ip: IPSTR, IP2STR(event-ip_info.ip)); xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); } }初始化WiFi的代码顺序非常关键void wifi_init_sta(void) { s_wifi_event_group xEventGroupCreate(); ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(cfg)); esp_event_handler_instance_t instance_any_id; ESP_ERROR_CHECK(esp_event_handler_instance_register( WIFI_EVENT, ESP_EVENT_ANY_ID, event_handler, NULL, instance_any_id)); ESP_ERROR_CHECK(esp_event_handler_instance_register( IP_EVENT, IP_EVENT_STA_GOT_IP, event_handler, NULL, instance_any_id)); wifi_config_t wifi_config { .sta { .ssid CONFIG_EXAMPLE_WIFI_SSID, .password CONFIG_EXAMPLE_WIFI_PASSWORD, .threshold.authmode WIFI_AUTH_WPA2_PSK, }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); EventBits_t bits xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY); if (bits WIFI_CONNECTED_BIT) { ESP_LOGI(TAG, wifi connected); } else { ESP_LOGE(TAG, wifi connect failed); } }其中有三个点容易踩坑单独拎出来说esp_netif_init()必须在WiFi初始化之前调用它是网络接口层的基础。顺序反了esp_wifi_init()直接报错。nvs_flash_init()也别漏。WiFi驱动内部会用到NVS存储校准数据不初始化会返回ESP_ERR_NVS_NO_FREE_PAGES之类的错误。标准做法是在app_main最开始调用如果返回ESP_ERR_NVS_NO_FREE_PAGES或ESP_ERR_NVS_NEW_VERSION_FOUND就执行nvs_flash_erase()再重新初始化。我把断线重连逻辑直接放在WIFI_EVENT_STA_DISCONNECTED里调用esp_wifi_connect()这对家用路由器来说够用了。官方推荐的esp_wifi_set_auto_reconnect也能用但显式重连能在日志里看到每次尝试状态调试阶段更直观。事件组的xEventGroupWaitBits在等待时是阻塞的对于main任务来说没问题。如果你后续想把连接逻辑放到后台任务里可以把超时时间改成pdMS_TO_TICKS(10000)之类的有限值避免整个系统卡死在这。3.2 esp_http_client请求天气APIWiFi通了之后下一步就是发HTTP请求。这里我用的是esp_http_client组件它的使用思路是先配置一个esp_http_client_config_t结构体然后初始化客户端执行请求通过事件回调接收响应数据。对于免费、无需注册的天气API我推荐用7Timer的接口http://www.7timer.info/bin/api.pl?lon113.17lat23.09productciviloutputjson这个接口的优势是纯HTTP明文传输不需要TLS证书省去esp_crt_bundle_attach之类的证书配置免费、无需API Key拿来测试最方便返回的JSON结构里包含temp2m2米处温度、rh2m相对湿度、wind10m10米风速风向、weather天气代码等字段刚好覆盖我们的需求。示例里我用的是广州的经纬度113.17, 23.09你可以换成自己城市的经纬度。请求代码static const char *WEATHER_URL http://www.7timer.info/bin/api.pl?lon113.17lat23.09productciviloutputjson; static char response_buffer[4096]; static int response_len 0; static esp_err_t http_event_handler(esp_http_client_event_t *evt) { switch (evt-event_id) { case HTTP_EVENT_ON_DATA: int copy_len evt-data_len; if (response_len copy_len sizeof(response_buffer)) { memcpy(response_buffer response_len, evt-data, copy_len); response_len copy_len; response_buffer[response_len] \0; } break; default: break; } return ESP_OK; } esp_err_t fetch_weather_data(void) { response_len 0; memset(response_buffer, 0, sizeof(response_buffer)); esp_http_client_config_t config { .url WEATHER_URL, .method HTTP_METHOD_GET, .event_handler http_event_handler, .timeout_ms 10000, }; esp_http_client_handle_t client esp_http_client_init(config); esp_err_t err esp_http_client_perform(client); if (err ESP_OK) { ESP_LOGI(TAG, HTTP response len %d, response_len); } else { ESP_LOGE(TAG, HTTP request failed: %s, esp_err_to_name(err)); } esp_http_client_cleanup(client); return err; }这里有一个非常关键的细节esp_http_client把响应体通过事件回调分片返回不是一次性塞给你的。在HTTP_EVENT_ON_DATA事件里拿到的evt-data只是当前这一片数据所以必须自己维护一个缓冲区用memcpy把每次拿到的数据追加到缓冲区末尾最后拼成完整响应。我遇到过很多人在这个问题上卡住——直接在HTTP_EVENT_ON_DATA里打日志发现日志在打印但缓冲区却是空的就是没做追加逻辑。另外response_buffer我用的是静态数组4KB对天气API来说够用了。但如果你要请求的接口返回数据较大比如包含未来7天逐小时的详细预报建议用动态内存分配或者加大缓冲区否则截断的JSON会直接导致解析失败。3.3 cJSON解析天气数据的关键细节有了完整的JSON字符串接下来用ESP-IDF内置的cJSON库解析。7Timer接口返回的数据结构类似这样{ product: civil, init: 2025041700, dataseries: [ { timepoint: 3, cloudcover: 1, seeing: 8, transparency: 5, lifted_index: -3, rh2m: 60, wind10m: { direction: NW, speed: 3 }, temp2m: 17, prec_type: none }, ... ] }解析的代码逻辑并不复杂但有几个坑必须注意void parse_weather_json(const char *json_str) { cJSON *root cJSON_Parse(json_str); if (root NULL) { ESP_LOGE(TAG, JSON parse error); return; } cJSON *dataseries cJSON_GetObjectItem(root, dataseries); if (dataseries NULL || !cJSON_IsArray(dataseries)) { ESP_LOGE(TAG, dataseries not found or not array); cJSON_Delete(root); return; } cJSON *first cJSON_GetArrayItem(dataseries, 0); if (first NULL) { ESP_LOGE(TAG, dataseries is empty); cJSON_Delete(root); return; } cJSON *temp2m cJSON_GetObjectItem(first, temp2m); cJSON *rh2m cJSON_GetObjectItem(first, rh2m); cJSON *wind10m cJSON_GetObjectItem(first, wind10m); if (cJSON_IsNumber(temp2m)) { ESP_LOGI(TAG, remote temp: %d, temp2m-valueint); } if (cJSON_IsNumber(rh2m)) { ESP_LOGI(TAG, remote humidity: %d, rh2m-valueint); } if (cJSON_IsObject(wind10m)) { cJSON *direction cJSON_GetObjectItem(wind10m, direction); cJSON *speed cJSON_GetObjectItem(wind10m, speed); if (cJSON_IsString(direction)) { ESP_LOGI(TAG, wind direction: %s, direction-valuestring); } if (cJSON_IsNumber(speed)) { ESP_LOGI(TAG, wind speed: %d m/s, speed-valueint); } } cJSON_Delete(root); }第一个坑每次调用cJSON_GetObjectItem之后先判断返回的指针是否为NULL再用cJSON_IsNumber、cJSON_IsString这类类型检查函数验证类型。如果API返回的结果和预期不符比如某个字段缺失或类型变了直接访问指针会崩溃。我一开始图省事拿到指针就取valueint结果某次网络代理把JSON内容改了程序直接重启。第二个坑解析完一定要调用cJSON_Delete(root)释放内存。cJSON会为整个JSON树分配动态内存不释放的话每次请求涨一块跑几小时就OOM了。ESP32的内存本来就不宽裕这种泄漏在长时间运行的项目里是致命的。第三个坑cJSON的数值类型默认是doublevalueint是把valuedouble转成int的结果。对于温度、湿度这种整数范围的数据用valueint没问题。但如果你要解析小数比如气压1013.25必须用valuedouble不然精度丢失。4. DHT11温湿度读取与显示整合4.1 简易DHT11驱动的实现思路DHT11的读取时序在ESP-IDF里属于比较经典的GPIO位操作。它的协议核心是单总线主机先发一个起始信号然后释放总线DHT11回应一个80us的低电平和80us的高电平接着连续输出40bit数据8bit湿度整数、8bit湿度小数、8bit温度整数、8bit温度小数、8bit校验和。每一位数据的表示方式是通过高电平持续的时间来区分的逻辑0高电平持续约26~28us逻辑1高电平持续约70us所以读DHT11的本质就是测量GPIO高电平的持续时间。在ESP-IDF里可以用esp_rom_delay_us()做微秒级延时配合gpio_get_level()轮询。驱动代码的核心逻辑#define DHT11_PIN GPIO_NUM_4 void dht11_start(void) { gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT_OD); gpio_set_level(DHT11_PIN, 0); esp_rom_delay_us(20000); // 拉低至少18ms gpio_set_level(DHT11_PIN, 1); esp_rom_delay_us(30); // 拉高20~40us gpio_set_direction(DHT11_PIN, GPIO_MODE_INPUT); } int dht11_read_byte(void) { int val 0; for (int i 0; i 8; i) { while (gpio_get_level(DHT11_PIN) 0); // 等待低电平结束 esp_rom_delay_us(40); // 延时判断位值 if (gpio_get_level(DHT11_PIN) 1) { val | (1 (7 - i)); } while (gpio_get_level(DHT11_PIN) 1); // 等待高电平结束 } return val; }这段代码我特地用GPIO_MODE_OUTPUT_OD开漏输出而不是推挽输出原因在于单总线协议里主机和设备都会拉低总线如果用推挽输出驱动能力过强可能会把DHT11拉低总线的动作打乱造成读取异常。开漏模式允许信号线被任意一方拉低行为上更接近协议设计要求。实际项目里不建议把DHT11驱动写得这么简陋更好的做法是用定时器捕获或者RMT驱动来测量脉宽准确性和稳定性都会大幅提升。不过对于这个项目的演示目的上面的轮询方式已经够用只要确保两次读取间隔不小于1秒——DHT11的数据更新频率就是1Hz你读得再快拿到的也是缓存值。读太快还会让传感器进入异常状态表现为连续读到0xFF或校验失败。4.2 main任务串接全流程上面的模块分开看都不复杂but把它们串起来就需要考虑任务结构和调度了。我设计了一个简单的顺序流初始化NVS初始化WiFi并等待连接连接成功后调用fetch_weather_data()获取天气调用parse_weather_json()解析读取DHT11本地温湿度把本地数据和远端天气数据一起打印到日志然后延时30秒进入下一轮。app_main的代码框架如下void app_main(void) { ESP_ERROR_CHECK(nvs_flash_init()); wifi_init_sta(); while (1) { if (fetch_weather_data() ESP_OK) { parse_weather_json(response_buffer); } int temp 0, humi 0; if (dht11_read_data(temp, humi) ESP_OK) { ESP_LOGI(TAG, local temp: %d.%d C, humi: %d.%d %%, temp / 10, temp % 10, humi / 10, humi % 10); } else { ESP_LOGE(TAG, dht11 read failed); } vTaskDelay(pdMS_TO_TICKS(30000)); } }这里的dht11_read_data是上面驱动代码的封装内部会完成起始时序、读取40bit数据、校验和验证。把校验和验证写进这个函数很重要DHT11的校验规则是“湿度整数湿度小数温度整数温度小数”的低8位等于校验字节如果校验失败就丢弃这批数据避免把错误数据打印出来误导人。日志输出大概是这样的效果I (1234) main: got ip: 192.168.1.100 I (1345) main: HTTP response len 2864 I (1345) main: remote temp: 27 I (1345) main: remote humidity: 82 I (1345) main: wind direction: S, wind speed: 2 m/s I (1350) main: local temp: 26.3 C, humi: 58.5 %到这一步整个项目就算跑通了。你会在串口里同时看到两个温度一个是ESP32所在房间的实时温度一个是目标城市的气象站温度。有意思吧我实测时经常遇到室内温度比室外低四五度的情况空调一开温差更明显。5. 调试经验从日志定位问题的三条排错链路5.1 WiFi连接失败的排查顺序WiFi连不上是这个项目里出现频率最高的问题。每次有朋友来问我“为什么连不上”我都让他们按下面这个顺序查第一步看NVS。启动日志里如果出现nvs: not initialized或者ESP_ERR_NO_FREE_PAGES先检查nvs_flash_init()调用返回值。注意首次烧录时NVS分区可能是新的正确做法是遇到ESP_ERR_NVS_NO_FREE_PAGES就调用nvs_flash_erase()擦除掉所有内容再重新nvs_flash_init()。第二步看WiFi事件日志。WIFI_EVENT_STA_DISCONNECTED事件的event_data里带有一个reason字段这是最重要的诊断信息。常见的reason码Reason含义处理201密码错误检查CONFIG_EXAMPLE_WIFI_PASSWORD154次握手超时检查路由器是否设置了MAC过滤2收到NACK路由器信号弱或AP未就绪203DHCP失败检查路由器DHCP池是否耗尽把reason打印出来基本上问题就定位了一半。超过80%的情况是密码填错或者没切换到2.4GHz频段。第三步看IP获取。WiFi连接成功不代表能上网只有IP_EVENT_STA_GOT_IP触发后网络才算真正可用。如果卡在这一步要么是路由器的DHCP服务有问题要么是ESP32的MAC地址被路由器拉黑了。5.2 JSON解析跑飞的常见原因JSON解析失败首先把response_buffer里的原始内容用ESP_LOGI打印出来看一眼。我发现很多“解析失败”其实是HTTP请求本身就没成功拿到的是一段HTML错误页。比如7Timer接口偶尔会被某些网络环境劫持返回运营商广告页这时候解析当然失败。另一个常见情况是响应被截断。我之前用2KB缓冲区请求一个预报接口数据量稍微一大JSON字符串被拦腰截断解析结果要么是NULL要么是乱码。解决办法是看HTTP_EVENT_ON_DATA里是否出现过buf NULL且data_len 0的情况或者检查response_len是否接近缓冲区上限。遇到这类问题直接加大缓冲区最省事。还有一点容易被忽略esp_http_client在拿到HTTP响应头后会调用事件回调通知应用层这时响应体还没开始传输。如果你在HTTP_EVENT_ON_HEADER事件里去解析JSON拿到的一定是空数据。一定要等到HTTP_EVENT_ON_DATA或HTTP_EVENT_DISCONNECTED之后再处理响应数据。5.3 DHT11数据异常的物理层原因软件逻辑看着没问题但DHT11读数就是不对这时候优先怀疑物理层。我在面包板上调试时遇到过三个典型问题一是没接上拉电阻。DHT11模块板上如果已经自带上拉电阻你就不用外接但裸的DHT11传感器必须外接。没上拉的表现是能读到数据但数值不稳定或者偶尔返回0xFF。二是杜邦线太长。DHT11的信号线如果超过20cm在时序要求严格的位判断环节容易出问题。特别是手机充电器、开关电源附近的电磁干扰会让高电平脉宽测出来偏大或偏小。解决办法是缩短线材尽量让传感器靠近开发板。三是GPIO模式设置不对。前面代码里我用的是开漏输出但有些人的驱动代码用的是GPIO_MODE_OUTPUT拉低总线之后再拉高时推挽输出的强驱动能力会让信号线电平爬升过快而DHT11在响应阶段也需要拉低总线两者冲突导致时序完全错乱。全部改成开漏输出后问题立刻消失。6. 换用HTTPS天气API的进阶建议与扩展方向6.1 生产环境尽量用HTTPS接口7Timer这种裸HTTP接口在开发验证阶段非常方便但真要部署到实际项目里我建议换成HTTPS的天气服务比如和风天气、OpenWeatherMap。理由很简单HTTP是明文传输API Key如果放在URL里在公共WiFi环境下很容易被中间人截获。用ESP-IDF的esp_http_client请求HTTPS接口相比HTTP多一个证书配置步骤。在esp_http_client_config_t加入esp_http_client_config_t config { .url https://devapi.qweather.com/v7/weather/now?location101010100keyYOUR_KEY, .method HTTP_METHOD_GET, .event_handler http_event_handler, .timeout_ms 10000, .crt_bundle_attach esp_crt_bundle_attach, };esp_crt_bundle_attach会让HTTP客户端使用ESP-IDF内置的CA证书包验证服务器身份。在menuconfig里需要确保Component config → mbedTLS → Enable ESP certificate bundle这个选项是开启的。默认一般是开着的但如果你裁剪过组件可能要回去检查一下。如果不开证书校验直接访问HTTPS接口最常见的结果是TLS握手失败日志里报mbedtls_ssl_setup failed或者ssl handshake failed。这个问题的本质是客户端没有可信任的根证书光把URL改成https是不够的。6.2 这整套框架还能怎么扩展现在你已经有了三个可复用的基础模块WiFi事件组同步、HTTP数据拉取、JSON解析。再往下扩展就是想象力问题了几个我觉得值得尝试的方向把数据和天气同时显示到OLED屏SSD1306用I2C接口的0.96寸OLED屏把本地温度、湿度、天气图标一次性展示出来就是一个完整的桌面天气站。改造成本很低只是把ESP_LOGI换成ssd1306_display_text的调用。上报到物联网平台把本地DHT11的数据用MQTT发布到巴法云或阿里云IoT平台在手机端用小程序或App实时查看。这里的HTTP请求框架可以留着用来拿天气新增MQTT连接模块负责上云。做室内外温差提醒通过比较本地温度和API返回的室外温度在温差超过阈值时驱动蜂鸣器或LED报警。比如夏天开空调时室内外温差超过8度容易感冒这个提醒就很有实际价值。我在结束这节之前再分享一个实际调试时的经验一定要把esp_http_client的超时时间设得合理一些。默认值可能是5秒对国内某些网络环境来说偏短。我实测过访问海外API在弱网下响应经常需要8~10秒如果超时设太短HTTP请求会频繁失败。另外串口日志的TAG要区分开WiFi日志、HTTP日志、JSON解析日志、DHT11日志各用一个TAG用idf.py monitor配合--filter参数过滤查看定位问题会高效很多。这个习惯我从做这个项目开始养成了后来调MQTT、调BLE都靠这个省了不少时间。
返回列表