资讯动态

ESP-IDF WiFi连接实战:从环境搭建到自动重连与问题排查

发布时间:2026/9/5 6:16:05 来源:尧图企业网站定制
1. 开发环境搭建先把ESP-IDF这块地基打牢1.1 为什么建议在Ubuntu 24.04上装v5.x版本先说一个我自己踩过的大坑。早几年用ESP-IDF的时候网上搜到的教程大半都基于v4.x照着敲一行idf.py set-target esp32c3倒是没啥问题可一旦用上较新的板子或者某些新的外设驱动老版本的组件库就开始拖后腿了。尤其最近这一两年乐鑫的迭代节奏很快v5.x在无线协议栈、事件处理、WiFi连接稳定性上的改进非常明显。如果你现在用的是Ubuntu 24.04我建议直接装v5.3或v5.4这两个比较新的版本。为什么不是最新版就最好因为ESP-IDF的master分支有时候会有一些还在验证阶段的新特性不排除会碰到编译工具链和系统Python版本不匹配的问题。Ubuntu 24.04自带的Python是3.12老版本ESP-IDF的Python依赖对3.12的支持并不好编译过程中可能会遇到pyparsing、construct这类包报错。v5.3、v5.4对Python 3.12的适配已经比较成熟装完基本能一路顺畅走到底。安装方式不复杂但有几个点容易被忽略mkdir -p ~/esp cd ~/esp git clone -b v5.4 --depth 1 https://github.com/espressif/esp-idf.git cd esp-idf ./install.sh esp32c3这里有个小细节esp32c3是可以改的比如用esp32或esp32s3它控制的是安装哪个芯片平台的工具链。后面运行idf.py set-target esp32c3设置目标芯片时如果你之前只装了esp32的工具链它会提示需要重新执行install脚本所以干脆在第一步就把目标芯片确认好。安装完成后不要忘了为当前终端加载环境变量source ~/esp/esp-idf/export.sh这个export.sh每次打开新终端都要手动执行嫌麻烦可以在~/.bashrc里加一行但我不建议这么做因为如果电脑上同时存在多个ESP-IDF版本自动加载反而会给不同项目之间制造版本冲突。1.2 VS Code插件这套组合拳的配置细节VS Code搭ESP-IDF插件是这两年主流的开发方式比裸用命令行写代码舒服太多。插件名字叫“Espressif IDF”直接在扩展商店搜索就能装。装完以后CtrlShiftP打开命令面板输入“ESP-IDF: Configure ESP-IDF Extension”进入配置向导。这个向导中比较关键的一步是选择ESP-IDF的安装方式我建议选“Find ESP-IDF in your system”然后手动指定~/esp/esp-idf那个目录。插件会根据当前打开的文件夹自动判断使用哪个IDF版本前提是你在配置时指定了正确的路径。还有一个经常被忽略的地方就是Python虚拟环境。ESP-IDF插件默认会创建一个独立的Python虚拟环境如果你之前手动使用过系统Python安装过一些包有概率出现环境和IDF自带依赖相互干扰的奇怪现象。遇到这类情况直接删掉插件创建的~/.espressif/python_env目录重新走一遍配置向导一般就能解决。工程目录方面我建议一个项目一个独立文件夹不要所有例程堆在一起。这样idf.py在编译的时候不会因为递归扫描到别的工程而出现问题VS Code的语法提示和跳转也会更快。1.3 独立工程与components子目录的协作方式ESP-IDF从v4.x开始大力推CMake构建系统工程结构整体上非常清晰wifi_sta_demo/ ├── CMakeLists.txt ├── main/ │ ├── CMakeLists.txt │ └── main.c ├── components/ │ ├── my_wifi_helper/ │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ └── my_wifi_helper.c │ └── ... └── partitions.csv (可选)顶层CMakeLists.txt里通常只需要一句话cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(wifi_sta_demo)components目录是ESP-IDF比较有特色的机制它允许你以“组件”为单位管理代码。如果一个WiFi连接逻辑你打算在多个工程里复用就不要塞在main.c里而是单独拎出来做成组件。这样做的好处是组件自带的include目录会被自动加入编译路径外部代码直接#include my_wifi_helper.h就能用。组件自己的CMakeLists.txt最简单写法是idf_component_register(SRCS my_wifi_helper.c INCLUDE_DIRS include)如果你引用了别的组件里的头文件比如后面要用esp_wifi.h、esp_event.h还要在PRIV_REQUIRES或REQUIRES里显式声明依赖否则编译时会报“找不到头文件”的错。这个细节是很多新手最头疼的明明代码看起来没问题CMake却一直报错其实就是组件依赖没写全。2. 先把WiFi连接拆清楚ESP-IDF的事件驱动模型2.1 建立连接的全链路从esp_netif到esp_event想要真正写好WiFi连接代码建议先理解ESP-IDF处理网络的整体思路。用一句话概括ESP-IDF把WiFi连接做成了一条事件流水线你的工作不是命令设备“连上WiFi”而是监听它发出的每一个状态信号。整个过程涉及三个核心模块esp_netif负责抽象网络接口可以理解为“一张虚拟网卡”。STA模式下创建的是WiFi工作站网卡AP模式下创建的是热点网卡以太网还会创建以太网网卡。esp_wifi底层WiFi驱动负责射频收发、扫描、连接管理。esp_event事件循环库负责把底层驱动产生的事件广播给所有注册了回调的程序。这三者之间的关系可以类比成一个快递系统esp_wifi是快递车它把“我已经发出包裹了”“包裹到了中转站”“包裹被退回了”这些状态更新上报给esp_event这个调度中心esp_netif是收件地址你的回调函数就是收件人。你不需要自己开车只需要告诉调度中心“货到了叫我没到也叫我”。2.2 STA模式初始化代码逐行拆解先看一段最基础的STA模式初始化代码这段代码是后面所有WiFi功能的地基#include string.h #include freertos/FreeRTOS.h #include freertos/event_groups.h #include esp_wifi.h #include esp_event.h #include esp_netif.h #include esp_log.h static const char *TAG wifi_sta; void wifi_sta_init(void) { // 1. 初始化网络接口层 ESP_ERROR_CHECK(esp_netif_init()); // 2. 创建默认事件循环 ESP_ERROR_CHECK(esp_event_loop_create_default()); // 3. 创建STA模式的网络接口 esp_netif_t *sta_netif esp_netif_create_default_wifi_sta(); assert(sta_netif); // 4. 初始化WiFi驱动包括WiFi协议栈和控制器 wifi_init_config_t wifi_init_config WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(wifi_init_config)); // 5. 注册事件回调 ESP_ERROR_CHECK(esp_event_handler_register( WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register( IP_EVENT, IP_EVENT_STA_GOT_IP, ip_event_handler, NULL)); // 6. 设置为STA模式并启动 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_start()); }逐行过一遍esp_netif_init()是网络接口层的总开关必须在所有网络操作之前调用否则后续创建接口会直接断言失败。esp_event_loop_create_default()创建了一个默认的事件循环之后注册WiFi事件、获取IP事件都走这个循环。esp_netif_create_default_wifi_sta()这个函数很关键它创建了一个STA类型的网络接口同时还会自动关联DHCP客户端。也就是说只要WiFi层连接成功网络接口层会自动向路由器申请IP地址。这也是为什么事件回调里能收到IP_EVENT_STA_GOT_IP的原因。WIFI_INIT_CONFIG_DEFAULT()返回一个默认的WiFi初始化配置结构体它包含了WiFi协议栈使用的缓冲区大小、动态内存分配参数、射频初始化参数等等。这个宏后面可以传入自定义配置但绝大多数场景不需要改。有个参数值得留意static_rx_buf_num和dynamic_rx_buf_num分别代表静态和动态RX缓冲区数量如果你发现WiFi吞吐量不够或者连接很不稳定可以考虑加大这几个值代价是内存占用上升。2.3 为什么默认模板里没有自动重连很多开发者在官方WiFi例程里会发现它只是连一次断线之后并不会自动重连。官方代码里有一个wifi_event_handler在WIFI_EVENT_STA_DISCONNECTED事件里只是打了一条日志ESP_LOGI(TAG, disconnect and retry...); esp_wifi_connect();但注意这只是“断线后手动再调用一次连接”并不是严谨的自动重连机制。如果你直接照抄遇到路由器重启、信号波动导致多次断线时程序会不断重连但有可能陷入“重连失败-再重连-再失败”的循环里。为什么官方不在底层直接做完整的自动重连因为ESP-IDF的设计哲学是把决策权交给应用层。底层只负责“上报状态”和“提供接口”至于何时重连、重连多少次、重连失败后要不要重启WiFi都是业务逻辑。官方例程只是一个可用示例不是完整的产品级方案。后面我会专门写一个带重试控制和信号提示的连接封装比官方模板更适合实际项目。3. 手写一个带自动重连与信号提示的WiFi连接器3.1 功能需求设定这个组件我平时在项目里反复用核心需求就三条能主动发起连接连接成功后通知上层“拿到IP了”。断线后自动重连但要有重试次数上限不能无限折腾。能感知WiFi信号强度RSSI方便做信号弱提示。先说“拿到IP”的时机问题。很多初学者以为esp_wifi_connect()返回ESP_OK就是连接成功了实际上这个返回值只代表“连接请求已经发出”最后能不能连通要看WIFI_EVENT_STA_CONNECTED事件。而这个事件也只表示WiFi层关联成功真正可以开始收发网络数据要以IP_EVENT_STA_GOT_IP事件为准。这个区别务必记清楚。3.2 核心代码实现下面这个组件我将它设计成标准模块内部使用FreeRTOS事件组来同步状态。头文件wifi_connect.h#ifndef WIFI_CONNECT_H #define WIFI_CONNECT_H #include esp_err.h #ifdef __cplusplus extern C { #endif typedef void (*wifi_connected_cb_t)(void *arg); typedef void (*wifi_disconnected_cb_t)(void *arg, int retry_count); esp_err_t wifi_connect_with_config(const char *ssid, const char *password); esp_err_t wifi_connect_register_callbacks(wifi_connected_cb_t connected_cb, wifi_disconnected_cb_t disconnected_cb, void *arg); int8_t wifi_connect_get_rssi(void); #ifdef __cplusplus } #endif #endif源文件wifi_connect.c#include string.h #include freertos/FreeRTOS.h #include freertos/task.h #include freertos/event_groups.h #include esp_wifi.h #include esp_event.h #include esp_netif.h #include esp_log.h #include nvs_flash.h #include wifi_connect.h #define WIFI_CONNECT_MAX_RETRY 5 #define WIFI_CONNECT_RETRY_DELAY_MS 2000 #define WIFI_CONNECT_EVENT_GOT_IP BIT0 #define WIFI_CONNECT_EVENT_FAILED BIT1 static const char *TAG wifi_connect; static EventGroupHandle_t s_wifi_event_group; static int s_retry_count 0; static wifi_connected_cb_t s_connected_cb NULL; static wifi_disconnected_cb_t s_disconnected_cb NULL; static void *s_user_arg NULL; static int8_t s_rssi -127; static void wifi_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_LOGI(TAG, STA started, connecting to AP...); esp_wifi_connect(); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_CONNECTED) { s_retry_count 0; ESP_LOGI(TAG, WiFi connected to AP, waiting for IP...); } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_DISCONNECTED) { if (s_retry_count WIFI_CONNECT_MAX_RETRY) { s_retry_count; ESP_LOGW(TAG, WiFi disconnected, retry %d/%d..., s_retry_count, WIFI_CONNECT_MAX_RETRY); vTaskDelay(pdMS_TO_TICKS(WIFI_CONNECT_RETRY_DELAY_MS)); esp_wifi_connect(); } else { ESP_LOGE(TAG, WiFi connect failed after %d retries., s_retry_count); if (s_disconnected_cb) { s_disconnected_cb(s_user_arg, s_retry_count); } } } else if (event_base WIFI_EVENT event_id WIFI_EVENT_STA_BEACON_TIMEOUT) { ESP_LOGW(TAG, Beacon timeout, likely weak signal.); } } static void ip_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { 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)); esp_netif_t *netif event-esp_netif; esp_netif_ip_info_t ip_info; esp_netif_get_ip_info(netif, ip_info); s_rssi 0; wifi_ap_record_t ap_info; if (esp_wifi_sta_get_ap_info(ap_info) ESP_OK) { s_rssi ap_info.rssi; ESP_LOGI(TAG, AP SSID: %s, RSSI: %d dBm, ap_info.ssid, ap_info.rssi); } if (s_connected_cb) { s_connected_cb(s_user_arg); } xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECT_EVENT_GOT_IP); } } esp_err_t wifi_connect_with_config(const char *ssid, const char *password) { if (ssid NULL || strlen(ssid) 0) { return ESP_ERR_INVALID_ARG; } s_wifi_event_group xEventGroupCreate(); ESP_ERROR_CHECK(nvs_flash_init()); esp_netif_init(); esp_event_loop_create_default(); esp_netif_t *sta_netif esp_netif_create_default_wifi_sta(); assert(sta_netif); wifi_init_config_t wifi_init_config WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(wifi_init_config); esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL); esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, ip_event_handler, NULL); memset(wifi_config, 0, sizeof(wifi_config)); strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid)); if (password) { strncpy((char *)wifi_config.sta.password, password, sizeof(wifi_config.sta.password)); } wifi_config.sta.threshold.authmode WIFI_AUTH_WPA2_PSK; esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(WIFI_IF_STA, wifi_config); esp_wifi_start(); return ESP_OK; } esp_err_t wifi_connect_register_callbacks(wifi_connected_cb_t connected_cb, wifi_disconnected_cb_t disconnected_cb, void *arg) { s_connected_cb connected_cb; s_disconnected_cb disconnected_cb; s_user_arg arg; return ESP_OK; } int8_t wifi_connect_get_rssi(void) { return s_rssi; }代码里有两个容易忽略的设计第一在WIFI_EVENT_STA_START里调用esp_wifi_connect()。原因是esp_wifi_start()只是启动WiFi驱动此时STA还处于未连接状态。等驱动真正准备好内核会发出STA_START事件此时调用esp_wifi_connect()才稳妥。如果直接在esp_wifi_start()后马上调用偶尔会因为驱动还没完全就绪而失败。第二重连之间加了vTaskDelay。这个延迟极其重要。在很多路由器上如果断线后立刻发起连接路由器还在清理上一个连接残留这时候重连大概率失败。加了2秒延迟给了路由器足够的时间释放资源重连成功率大大提高。3.3 事件回调里到底能不能反复调用esp_wifi_connect有个问题经常被问到在WIFI_EVENT_STA_DISCONNECTED的事件回调里直接调用esp_wifi_connect()安全吗从ESP-IDF的实现来说事件回调是在事件任务上下文里被调用的不是中断上下文所以调用大多数WiFi API是允许的。但允许不等于推荐。如果回调函数里用了阻塞式操作比如vTaskDelay、互斥锁等待可能会卡住事件任务本身影响其他事件的处理。我在上面的代码里在回调里用了vTaskDelay(2000ms)理论上确实会阻塞事件任务2秒。这个在重连场景里是可以接受的因为断线期间本来就没有太多事件需要处理。但如果你的系统对事件响应时效要求很高建议改用定时器把esp_wifi_connect()放到一个单独的任务或esp_timer的回调里执行不要阻塞事件循环。这里也分享一个我在日志里发现的现象当WiFi断线事件触发时如果信号还是很弱驱动可能会在极短时间内上报多次STA_DISCONNECTED。如果不加重试次数控制代码里的esp_wifi_connect()会被反复调用导致连接请求堆积。这也是我在组件里用retry_count做上限的原因。4. 连接参数与网络配置细节这些坑多半不是代码问题4.1 ssid和password之外还有哪些参数值得关注配置WiFi连接时多数人只设置ssid和password这两个字段如果只连家用路由器其实够用了。但一旦涉及跨厂商AP、企业级网络或者信道拥挤的场景就要深入了解wifi_config_t里的其他成员。wifi_config_t的核心结构是wifi_sta_config_t常用字段如下字段说明典型值/注意事项ssidWiFi名称SSID最长32字节不要带多余空格passwordWiFi密码WPA2长度8~63字节空网络可设为NULLscan_method扫描方式WIFI_FAST_SCAN或WIFI_ALL_CHANNEL_SCAN默认快速扫描只搜1/6/11threshold.authmode连接的最低认证方式建议设为WPA2或WPA3sae_pwe_h2eWPA3-SAE的关键参数对WPA3网络非常重要failure_retry_cnt失败重试次数默认-1表示无限重试channel指定信道为0时表示自动扫描固定的环境可以手动指定提速scan_method经常被忽视。默认是WIFI_FAST_SCAN它会按信道顺序扫描最快找到第一个匹配SSID的AP就连接。如果周围有多个同名AP比如企业里的多个房间共用同一SSID快速扫描可能连上信号很差的那个。想要连信号最好的那个把scan_method改成WIFI_ALL_CHANNEL_SCAN并确保threshold.rssi设为一个合理的值比如-70dBm。channel参数在省电场景下很有用。如果产品的AP固定在一个信道手动配置信道可以省去扫描时间连接速度会快很多。缺点是信道一旦变动代码里的固定信道就失效了。4.2 DHCP拿到IP之后还需要手动配置DNS么这是另一个被忽略的问题。ESP-IDF的DHCP客户端在默认配置下会从路由器自动获取IP、网关和DNS。但在特殊场景下比如路由器上手动配置了自定义DNS或者设备需要固定使用某个公共DNS就需要在代码里覆盖默认配置。手动配置静态IP和DNS的做法如下esp_netif_t *netif esp_netif_get_handle_from_ifkey(WIFI_STA_DEF); esp_netif_dhcpc_stop(netif); // 先停止DHCP客户端 esp_netif_ip_info_t ip_info {0}; ip_info.ip.addr ESP_IP4TOADDR(192, 168, 1, 100); ip_info.gw.addr ESP_IP4TOADDR(192, 168, 1, 1); ip_info.netmask.addr ESP_IP4TOADDR(255, 255, 255, 0); esp_netif_set_ip_info(netif, ip_info); esp_netif_dns_info_t dns_info {0}; dns_info.ip.type ESP_IPADDR_TYPE_V4; dns_info.ip.u_addr.ip4.addr ESP_IP4TOADDR(223, 5, 5, 5); // 示例DNS esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, dns_info); esp_netif_dhcpc_start(netif); // 重新启动如果想恢复DHCP需要注意这个流程应该在esp_netif_create_default_wifi_sta()之后、WiFi连接建立之前执行而且不要和DHCP自动获取的逻辑混用。如果你的应用只是普通家用路由器场景强烈建议不要动这层逻辑交给DHCP就好。手动配置一旦出错排查成本比自动获取高一个量级。4.3 企业WiFi与隐藏热点很多开发板在连接到典型的WPA2-PSK家用路由器时很顺利一旦到了公司、学校这种企业WiFi环境就蒙了。企业WiFi通常使用WPA2-Enterprise认证方式是PEAP或EAP-TLS这已经不是wifi_config_t里填个密码就能解决的范畴了。ESP-IDF里针对企业WiFi提供了esp_wifi_sta_enterprise_enable()使用前需要先调用esp_wifi_sta_disable()停止STA然后配置esp_eap_client_config_t、esp_wpa2_config_t等结构体。这块代码不复杂但要引入额外的安全依赖组件如果你只是个人学习建议先在家用WPA2网络下打通基础流程再考虑企业网络的扩展。隐藏热点又是另一个情况。路由器开了“隐藏SSID”后设备无法通过扫描发现这个网络需要手动设置wifi_config.sta.ssid和channel并且把wifi_config.sta.scan_method设为WIFI_FAST_SCAN。有些路由器固件对隐藏网络的广播处理不标准设备连接时可能会卡在SCANNING状态很久这时候可以尝试在ssid里额外指定校验位或者在AP侧开启“兼容模式”。5. 实网连接里的常见问题解决记录5.1 路由器2.4G/5G混频导致反复断开我第一块ESP32-C3开发板拿回家测试时遇到的第一个怪现象是程序烧录进去日志能打“connected”但几秒钟后就断线反反复复完全连不上局域网。排查了很久最后发现是家里路由器开启了“双频合一”——2.4G和5G用同一个SSID。ESP32-C3本身只支持2.4G当它在2.4G信道上连接成功后路由器可能会引导设备漫游到5G频段但芯片根本没有5G射频于是只能断开重连循环往复。解决办法有两种一种是登录路由器管理页面把2.4G和5G的SSID分开另一种是代码层面只固定扫描2.4G信道。第一种方法更干净如果你手头的设备只做产品原型直接改路由器设置能把很多排查时间省下来。这个问题的鲁棒性层面也值得思考。产品做出来要卖给用户你不可能要求用户专门调整路由器。所以在代码里要把断开重连逻辑写得足够健壮即使路由器再“折腾”设备也能在重试几次后稳定连上。5.2 射频参数与天线选择对连接稳定性的影响经常看到有人抱怨ESP32信号差、连接不稳其实问题的根源有不少在射频部分。如果你的开发板带的是PCB天线或陶瓷天线天线周围不要大面积覆铜也不要被金属外壳完全包裹。ESP32模块的天线区域通常会在模块边上有明确的一小段露铜区那就是天线辐射区。焊接排针时尽量让天线区悬空不要紧贴杜邦线或者其他导线。如果用外置天线IPEX接口要确保天线和模块之间用的是同轴馈线长度尽量短。不要为了“看起来整洁”把天线和馈线绕成一团这会使驻波比变差发射功率大部分消耗在馈线上信号自然好不了。代码层面可以通过esp_wifi_set_ps(WIFI_PS_NONE)关闭WiFi省电模式。省电模式默认是开启的这意味着模块会周期性休眠射频在低速率场景下没问题但在音频传输、TCP长连接这种对实时性要求高的场景里会产生周期性延迟通断。关闭省电模式的代价是功耗上升但如果你的设备不是电池供电直接关掉。5.3 日志与调试三板斧调WiFi连接除了一张串口日志还有几个非常实用的命令和工具。第一板斧开启详细日志。ESP_LOGW、ESP_LOGD级别不够用时在sdkconfig里把日志级别调到VERBOSEidf.py menuconfig # Component config - Log output - Default log verbosity - Verbose重新编译烧录后你会在串口终端看到WiFi事件和信息帧的完整打印这些信息对定位“为什么连不上”“什么时候断开”帮助极大。第二板斧使用esp_wifi_get_ap_info()获取当前连接AP的信息。这个函数返回一个wifi_ap_record_t结构体里面包含SSID、RSSI、信道、认证方式等关键参数。很多看似玄学的连接异常一查RSSI就明白了。如果RSSI低于-80dBm基本可以放弃“通过代码优化连接”先去改善物理环境。第三板斧用idf.py monitor的过滤功能。日志太长时不要用肉眼硬刷ctrlt后输入过滤条件只保留WiFi相关日志idf.py monitor进入monitor后按CtrlT再按F键输入“wifi”或“WIFI”监视器就只显示匹配WiFi关键字的日志。连续观察几次断开前后的日志基本能把问题定位在驱动层、网络层还是应用层。调试WiFi连接时我个人还有一个习惯就是先不做任何业务逻辑只跑一个最小连接程序用手机热点测试一遍再用家用路由器测试一遍最后才接入真实产品环境。手机热点没有双频合一也没有复杂的访问控制能够提供一个足够干净的测试基准。如果在这样干净的条件下仍然连接异常那问题多半出在模块硬件或固件配置上这时候再回头查天线、查电源、查配置方向会明确很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价