4、连接WiFi网络实战:编写第一个连接函数、处理连接超时、获取IP地址

好,咱们正式开始写代码了。

前面几章我们把WiFi的工作模式、状态机、事件驱动这些概念捋了一遍。说实话,光看理论容易犯困。我当年刚学ESP32的时候,也是看了三天文档,结果一上电连不上路由器,心态直接崩了。

所以这一章,咱们直接动手。目标很明确:写一个能用的WiFi连接函数。它要能连上指定的路由器,能处理连不上的情况(超时),最后还能拿到IP地址。

4.1 第一个连接函数:从阻塞到非阻塞

很多人写WiFi连接,第一版代码长这样:

# 初学者写法 —— 千万别这么用
import network
import time

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect("MyWiFi", "password123")

while not wlan.isconnected():
    time.sleep(0.5)
    print("正在连接...")

print("连接成功!")

这段代码能跑吗?能。但问题很大——它把整个程序卡死了。

你想想看,如果WiFi信号弱,或者密码输错了,这个while循环会一直卡在那里。你的传感器读不了,LED闪不了,按键也响应不了。整个设备就像死机了一样。

我在项目里见过有人用这种写法做产品原型,结果测试的时候路由器一重启,设备就再也连不回来了——因为循环卡死,连看门狗都喂不了。

⚠ 阻塞式连接是生产环境的大忌。 它会阻塞整个事件循环,导致系统无法响应其他任务。在IoT设备中,这往往意味着设备失联。

4.2 非阻塞连接:用超时机制替代死循环

正确的做法是什么?设定一个超时时间。到了时间连不上,就返回失败,让主循环继续跑。

import network
import time

def connect_wifi(ssid, password, timeout=10):
    """
    连接WiFi网络,带超时控制
    :param ssid: 路由器名称
    :param password: 密码
    :param timeout: 超时秒数,默认10秒
    :return: True 连接成功,False 连接失败
    """
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)

    if wlan.isconnected():
        print("已经连接,跳过")
        return True

    wlan.connect(ssid, password)
    start_time = time.time()

    while not wlan.isconnected():
        # 检查是否超时
        if time.time() - start_time > timeout:
            print(f"连接超时({timeout}秒),请检查SSID/密码")
            return False
        time.sleep(0.5)
        print(".", end="")

    print("\n连接成功!")
    return True
💡 个人习惯: 我一般把超时设在10-15秒。太短了,信号弱的时候连不上;太长了,用户体验差。10秒是个不错的平衡点。

这个函数的核心逻辑很简单:

  • 先检查是否已经连接,避免重复操作
  • 发起连接后,进入循环等待
  • 每次循环检查是否超时
  • 超时了就返回False,不卡死

嗯,这里要注意:time.time()在MicroPython里返回的是秒级时间戳,精度够用。如果你需要更精细的控制,可以用time.ticks_ms()。

4.3 获取IP地址:连接成功的标志

连接成功之后,我们得拿到IP地址。没有IP,设备在局域网里就是透明的。

def get_ip_info(wlan):
    """
    获取当前WiFi连接的IP信息
    :param wlan: network.WLAN 对象
    :return: dict 包含IP、子网掩码、网关、DNS
    """
    if not wlan.isconnected():
        print("未连接,无法获取IP")
        return None

    config = wlan.ifconfig()
    # config 返回一个元组:(ip, subnet, gateway, dns)
    info = {
        "ip": config[0],
        "subnet": config[1],
        "gateway": config[2],
        "dns": config[3]
    }
    return info

调用方式也很简单:

if connect_wifi("MyWiFi", "password123"):
    wlan = network.WLAN(network.STA_IF)
    ip_info = get_ip_info(wlan)
    print(f"IP地址: {ip_info['ip']}")
    print(f"网关: {ip_info['gateway']}")

我曾经遇到过一个坑:有些路由器会分配169.254.x.x的APIPA地址。这其实是设备没拿到有效IP的表现。所以拿到IP后,最好检查一下是不是以"169.254"开头。

🔑 关键点: wlan.ifconfig()返回的元组顺序是固定的——IP、子网掩码、网关、DNS。别搞反了,我见过有人把网关当IP用的。

4.4 完整连接流程:把函数串起来

现在我们把两个函数组合成一个完整的连接流程:

def wifi_connect_full(ssid, password, timeout=10):
    """
    完整的WiFi连接流程:连接 + 获取IP
    """
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)

    # 第一步:连接
    if not connect_wifi(ssid, password, timeout):
        return False, None

    # 第二步:获取IP
    ip_info = get_ip_info(wlan)
    if ip_info:
        print(f"连接成功,IP: {ip_info['ip']}")
        return True, ip_info
    else:
        print("连接成功但无法获取IP")
        return False, None

这个函数返回两个值:(是否成功, IP信息字典)。调用方可以根据返回值做后续处理。

4.5 连接状态与IP获取的SVG流程图

下面这张图把整个流程画清楚了。我建议你把它存下来,写代码的时候对照着看。

WiFi连接与IP获取流程图 WiFi连接与IP获取流程图 开始连接 激活STA模式 + 发起连接 循环等待 + 超时判断 是否超时? 返回 False 获取IP地址 (ifconfig) 返回 True + IP信息 说明 超时后返回False 不阻塞主循环 IP获取失败也返回False

4.6 常见问题与避坑指南

问题 原因 解决方法
一直连不上 SSID或密码错误 检查SSID是否包含空格、特殊字符;密码是否区分大小写
连接成功但没IP DHCP服务器未响应 检查路由器DHCP是否开启;尝试静态IP
IP是169.254.x.x DHCP协商失败 重启路由器或设备;检查网线/信号强度
连接超时但WiFi正常 路由器开启了MAC过滤 在路由器后台添加设备MAC地址到白名单
⚠ 我曾经踩过的坑: 有一次设备在实验室连得好好的,一到客户现场就连不上。查了半天,发现客户的路由器开启了“5G优先”,而ESP32只支持2.4G。最后在SSID后面加了“_2.4G”才解决。所以,生产环境一定要确认路由器频段兼容性。

4.7 本章小结

这一章我们干了三件事:

  • 写了一个非阻塞的连接函数,用超时机制替代了死循环
  • 实现了IP地址获取,并封装成可复用的函数
  • 把两个函数组合成完整流程,返回成功状态和IP信息

说实话,这段代码看起来简单,但它是整个WiFi重连机制的基础。后面章节的自动重连、状态切换、事件回调,全都要依赖这个核心函数。

你现在可以把它烧到板子上试试。连上了,恭喜你——你的设备已经正式接入网络了。