ESP32的应用范围有多广,相信不用多说大家都明白,尤其是在物联网领域,所以今天我们就来讲解WIFI的配置、连接及其简单应用。
跟往常一样,我们还是先介绍WIFI的相关知识
一、硬件与软件基础
1.1 ESP32-C3无线特性
ESP32-C3是乐鑫推出的RISC-V架构物联网芯片,配备:
- 2.4 GHz WiFi 4(802.11b/g/n)
- Bluetooth 5 LE
- 低功耗设计(仅22uA深度睡眠电流)
1.2 MicroPython优势
- 交互式开发(REPL)
- 简化硬件操作
- 丰富的网络库支持
- 与CPython高度兼容
二、MicroPython WiFi模块详解
2.1 核心对象说明
import network
wlan = network.WLAN(network.STA_IF) # 创建工作站对象
wlan.active(True) # 激活接口
2.2 关键方法解析
方法 | 参数 | 说明 |
connect() | ssid, password | 连接目标AP |
disconnect() | 断开当前连接 | |
scan() | 扫描可用网络 | |
isconnected() | 返回连接状态 | |
ifconfig() | [(ip, mask, gw, dns)] | 获取/设置网络参数 |
三、WiFi连接实战
3.1 基础连接代码
def configwifi(self,ssid,password):
if not self.wlan.isconnected():
self.ssid = ssid
self.password = password
print(" 正在连接到",ssid)
self.wlan.connect(ssid,password)
max_wait = 10
while max_wait > 0:
if self.wlan.isconnected():
break
max_wait -= 1
print("等待连接……",max_wait)
time.sleep(1)
if not self.wlan.isconnected():
return False
# print("连接失败")
else:
return True
# print("连接成功")
# print("网络配置:",self.wlan.ifconfig())
3.2 增强型连接(含错误处理)
def smart_connect(ssid, password):
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if wlan.isconnected():
wlan.disconnect()
visible_nets = {net[0].decode(): net for net in wlan.scan()}
if ssid not in visible_nets:
raise ValueError(f"{ssid} not found")
authmode = visible_nets[ssid][4]
print(f"Security: {authmode}") # 0=OPEN, 1=WEP, etc.
try:
wlan.connect(ssid, password)
for _ in range(15):
if wlan.isconnected():
print(f"IP: {wlan.ifconfig()[0]}")
return True
time.sleep(1)
return False
except OSError as e:
print(f"Connection error: {e}")
return False
3.3 增加网络配置保存、重启时自动连接功能
self.wlan = network.WLAN(network.STA_IF)
self.wlan.active(True)
ssid,password = self.load_wificonfig()
if ssid and password:
print(ssid,password)
self.wlan.connect(ssid,password)
self.ssid = ssid
self.password = password
def save_wificonfig(self):
with open ("wificonfig.txt","w") as f:
f.write(self.ssid+"\n")
f.write(self.password+"\n")
def load_wificonfig(self):
try:
with open("wificonfig.txt","r") as f:
ssid = f.readline().strip()
password = f.readline().strip()
return ssid,password
except:
return None,None
四、网络时间同步
4.1 NTP协议原理
- 使用UDP 123端口
- 支持时间服务器分层架构
- 精度通常在毫秒级
4.2 NTP服务器选择
- 国内推荐:ntp.aliyun.com
- 国际通用:pool.ntp.org
4.3 MicroPython实现代码
def sync_time_with_timezone(self,offset=8):
# 同步时间并设置时区
# offset: 时区偏移(小时),东八区为8
if not self.wlan.isconnected():
# print("未连接到网络,请检测网络")
return False
try:
ntptime.host = "ntp.aliyun.com"
ntptime.settime()
rtc = RTC()
tm = list(rtc.datetime())
# 调整时区
tm[4] += offset # 增加小时偏移
if tm[4] >= 24:
tm[4] -= 24
tm[2] += 1 # 增加一天
rtc.datetime(tuple(tm))
self.is_time_sync = True
# print("时间同步成功(UTC+%d):" % offset, rtc.datetime())
return True
except Exception as e:
self.is_time_sync = False
print("时间同步失败:", e)
return False
4.4 增加时间日期格式化输出
def get_datetime(self,rtc=None):
if self.is_time_sync == False:
if self.sync_time_with_timezone() == False:
return ""
if rtc is None:
rtc = RTC()
return rtc.datetime()
def format_datetime(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(t[0],t[1],t[2],t[4],t[5],t[6])
def format_date(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:04d}-{:02d}-{:02d}".format(t[0],t[1],t[2])
def format_time(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:02d}:{:02d}:{:02d}".format(t[4],t[5],t[6])
4.5 增加定时同步时间功能
def periodic_time_sync(self,interval=3600):
#定期同步时间,interval :同步时间间隔(秒),默认1小时
if interval != 0:
self.time_sync = interval
if self.is_time_sync:
if self.last_synctime != None:
rtc = RTC()
if self.datetime_to_seconds(rtc.datetime()) - self.datetime_to_seconds(self.last_synctime) < self.time_sync:
return
if self.auto_sync_time():
# print("同步时间成功,当前时间:",self.format_time())
rtc = RTC()
self.last_synctime = rtc.datetime()
print("同步时间:",self.format_datetime())
这个功能的实现方式比较多,我们后续的课程会探讨在中断处理程序中实现定时同步时间及获取天气信息。
五、WIFI及时间同步的封装
有关知识点我们基本介绍了,直接上代码:(该部分代码包括上面全部的代码功能)
import network
import time
import ntptime
import urequests
from machine import RTC
class esp32_wifi(object):
ssid=""
password = ""
last_synctime = None
time_sync = 3600 # 自动同步时间间隔(秒)
is_time_sync = False
def __init__(self,mode="STA_MODE",esp32ssid="ESP32C3-AP",esp32psw = "",esp32authmode=network.AUTH_WPA_WPA2_PSK):
if mode == "STA_MODE":
self.wlan = network.WLAN(network.STA_IF)
self.wlan.active(True)
ssid,password = self.load_wificonfig()
if ssid and password:
print(ssid,password)
self.wlan.connect(ssid,password)
self.ssid = ssid
self.password = password
elif mode == "AP_MODE":
self.ap = network.WLAN(network.AP_IF)
self.ap.config(essid = esp32ssid,password = esp32psw,authmode = esp32authmode)
self.ap.active(True)
self.ssid = esp32ssid
self.password = esp32psw
self.authmode = esp32authmode
print("ESP32C3 AP IP地址:",self.ap.ifconfig()[0])
def scanwifi(self):
networks = self.wlan.scan()
for net in networks:
print(net[0],net[3])
def configwifi(self,ssid,password):
if not self.wlan.isconnected():
self.ssid = ssid
self.password = password
print(" 正在连接到",ssid)
self.wlan.connect(ssid,password)
max_wait = 10
while max_wait > 0:
if self.wlan.isconnected():
break
max_wait -= 1
print("等待连接……",max_wait)
time.sleep(1)
if not self.wlan.isconnected():
return False
# print("连接失败")
else:
return True
# print("连接成功")
# print("网络配置:",self.wlan.ifconfig())
def check_wifi(self):
if self.wlan.isconnected():
return True
# print("已连接到网络")
# print("IP地址:",self.wlan.ifconfig()[0])
# print("子网掩码:",self.wlan.ifconfig()[1])
# print("网关:",self.wlan.ifconfig()[2])
# print("DNS:",self.wlan.ifconfig()[3])
else:
return False
# print("未连接到网络")
# self.configwifi(self.ssid,self.password)
def save_wificonfig(self):
with open ("wificonfig.txt","w") as f:
f.write(self.ssid+"\n")
f.write(self.password+"\n")
def load_wificonfig(self):
try:
with open("wificonfig.txt","r") as f:
ssid = f.readline().strip()
password = f.readline().strip()
return ssid,password
except:
return None,None
def sync_ntp_time(self):
if not self.wlan.isconnected():
print("未连接到网络,请检测网络")
return False
try:
ntptime.host = "ntp.aliyun.com"
ntptime.settime()
rtc = RTC()
print("同步成功!当前时间:",rtc.datetime())
self.is_time_sync = True
return True
except Exception as e:
self.is_time_sync = False
print("时间同步失败:",e)
return False
def sync_time_with_timezone(self,offset=8):
# 同步时间并设置时区
# offset: 时区偏移(小时),东八区为8
if not self.wlan.isconnected():
# print("未连接到网络,请检测网络")
return False
try:
ntptime.host = "ntp.aliyun.com"
ntptime.settime()
rtc = RTC()
tm = list(rtc.datetime())
# 调整时区
tm[4] += offset # 增加小时偏移
if tm[4] >= 24:
tm[4] -= 24
tm[2] += 1 # 增加一天
rtc.datetime(tuple(tm))
self.is_time_sync = True
# print("时间同步成功(UTC+%d):" % offset, rtc.datetime())
return True
except Exception as e:
self.is_time_sync = False
print("时间同步失败:", e)
return False
def get_datetime(self,rtc=None):
if self.is_time_sync == False:
if self.sync_time_with_timezone() == False:
return ""
if rtc is None:
rtc = RTC()
return rtc.datetime()
def format_datetime(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(t[0],t[1],t[2],t[4],t[5],t[6])
def format_date(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:04d}-{:02d}-{:02d}".format(t[0],t[1],t[2])
def format_time(self):
t = self.get_datetime()
if t == "":
return ""
else:
return "{:02d}:{:02d}:{:02d}".format(t[4],t[5],t[6])
def auto_sync_time(self,max_retries = 3,retry_delay=10):
#max_retries: 最大重试次数
#retry_delay:重试间隔(秒)
retry = 0
while retry < max_retries:
if self.sync_time_with_timezone(8):
return True
retry += 1
time.sleep(retry_delay)
return False
def days_in_month(self,year, month):
# 返回某年某月的天数(修正闰年)
if month == 2:
return 29 if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0) else 28
return [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month-1]
def datetime_to_seconds(self,dt):
year, month, day, _, hour, minute, second, _ = dt
total_days = 0
for y in range(2000, year):
total_days += 366 if (y % 4 == 0 and y % 100 != 0) or (y % 400 == 0) else 365
for m in range(1, month):
total_days += self.days_in_month(year, m)
total_days += day - 1
return total_days * 86400 + hour * 3600 + minute * 60 + second
def periodic_time_sync(self,interval=3600):
#定期同步时间,interval :同步时间间隔(秒),默认1小时
if interval != 0:
self.time_sync = interval
if self.is_time_sync:
if self.last_synctime != None:
rtc = RTC()
if self.datetime_to_seconds(rtc.datetime()) - self.datetime_to_seconds(self.last_synctime) < self.time_sync:
return
if self.auto_sync_time():
# print("同步时间成功,当前时间:",self.format_time())
rtc = RTC()
self.last_synctime = rtc.datetime()
print("同步时间:",self.format_datetime())
六、i2c驱动aht20并显示
aht20的介绍比较多,有兴趣的朋友可以参考:CH592F /CH582通过硬件IIC读写AHT10 /AHT20
直接上代码:
# The MIT License (MIT)
#
# Copyright (c) 2020 Kattni Rembor for Adafruit Industries
# Copyright (c) 2020 Andreas Bühl
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
MicroPython driver for the AHT10 and AHT20 Humidity and Temperature Sensor
Author(s): Andreas Bühl, Kattni Rembor
"""
import utime
from micropython import const
class AHT10:
"""Interface library for AHT10/AHT20 temperature+humidity sensors"""
AHTX0_I2CADDR_DEFAULT = const(0x38) # Default I2C address
AHTX0_CMD_INITIALIZE = 0xE1 # Initialization command
AHTX0_CMD_TRIGGER = const(0xAC) # Trigger reading command
AHTX0_CMD_SOFTRESET = const(0xBA) # Soft reset command
AHTX0_STATUS_BUSY = const(0x80) # Status bit for busy
AHTX0_STATUS_CALIBRATED = const(0x08) # Status bit for calibrated
def __init__(self, i2c, address=AHTX0_I2CADDR_DEFAULT):
utime.sleep_ms(20) # 20ms delay to wake up
self._i2c = i2c
self._address = address
self._buf = bytearray(6)
self.reset()
if not self.initialize():
raise RuntimeError("Could not initialize")
self._temp = None
self._humidity = None
def reset(self):
"""Perform a soft-reset of the AHT"""
self._buf[0] = self.AHTX0_CMD_SOFTRESET
self._i2c.writeto(self._address, self._buf[0:1])
utime.sleep_ms(20) # 20ms delay to wake up
def initialize(self):
"""Ask the sensor to self-initialize. Returns True on success, False otherwise"""
self._buf[0] = self.AHTX0_CMD_INITIALIZE
self._buf[1] = 0x08
self._buf[2] = 0x00
self._i2c.writeto(self._address, self._buf[0:3])
self._wait_for_idle()
if not self.status & self.AHTX0_STATUS_CALIBRATED:
return False
return True
@property
def status(self):
"""The status byte initially returned from the sensor, see datasheet for details"""
self._read_to_buffer()
return self._buf[0]
@property
def relative_humidity(self):
"""The measured relative humidity in percent."""
self._perform_measurement()
self._humidity = (
(self._buf[1] << 12) | (self._buf[2] << 4) | (self._buf[3] >> 4)
)
self._humidity = (self._humidity * 100) / 0x100000
return self._humidity
@property
def temperature(self):
"""The measured temperature in degrees Celcius."""
self._perform_measurement()
self._temp = ((self._buf[3] & 0xF) << 16) | (self._buf[4] << 8) | self._buf[5]
self._temp = ((self._temp * 200.0) / 0x100000) - 50
return self._temp
def _read_to_buffer(self):
"""Read sensor data to buffer"""
self._i2c.readfrom_into(self._address, self._buf)
def _trigger_measurement(self):
"""Internal function for triggering the AHT to read temp/humidity"""
self._buf[0] = self.AHTX0_CMD_TRIGGER
self._buf[1] = 0x33
self._buf[2] = 0x00
self._i2c.writeto(self._address, self._buf[0:3])
def _wait_for_idle(self):
"""Wait until sensor can receive a new command"""
while self.status & self.AHTX0_STATUS_BUSY:
utime.sleep_ms(5)
def _perform_measurement(self):
"""Trigger measurement and write result to buffer"""
self._trigger_measurement()
self._wait_for_idle()
self._read_to_buffer()
class AHT20(AHT10):
AHTX0_CMD_INITIALIZE = 0xBE # Calibration command
利用aht20模块读取当前的温湿度并显示:
from ahtx0 import AHT20
i2c = I2C(0,scl=Pin(2),sda=Pin(3),freq=200000)
aht20_sensor = AHT20(i2c)
wendu = aht20_sensor.temperature
shidu = aht20_sensor.relative_humidity
oled.show_chinese("温",3,43,1)
oled.text("{:>4.1f}".format(wendu),23,48,1)
oled.show_chinese("湿",64,43,1)
oled.text("{:>4.1f}".format(shidu),84,48,1)
aht20的硬件连接 :
aht20 | ESPC3 |
scl | IO2 |
sda | IO3 |
调用以上模块并显示的代码:
from machine import I2C,SPI,Pin,Timer
import time
from neopixel import NeoPixel
from ssd1306 import SSD1306_SPI
from esp32_wifi import esp32_wifi
from ahtx0 import AHT20
spi = SPI(1,baudrate = 8000000,
polarity = 0,
phase = 0,
sck=Pin(4),
mosi=Pin(5),
miso=None)
dc = Pin(6,Pin.OUT)
cs = Pin(7,Pin.OUT)
rst= Pin(10,Pin.OUT)
oled = SSD1306_SPI(128, 64, spi,dc,rst,cs) #OLED显示屏初始化:128*64分辨率
i2c = I2C(0,scl=Pin(2),sda=Pin(3),freq=200000)
aht20_sensor = AHT20(i2c)
# 定义全局变量
WS2812_NUM = 1
WS2812_LED_LEVEL = 0x10
G = 0xff
R = 0
B = 0
pin = Pin(8, Pin.OUT) #配置GPIO8为输出端口,控制ws2812灯珠的DIN信号
np = NeoPixel(pin, WS2812_NUM,3,1)
def ws2812_LED_On(phase):
global G,R,B
for i in range(WS2812_NUM - 1):
np[WS2812_NUM -1 - i] = np[WS2812_NUM - 2 - i]
if phase < WS2812_LED_LEVEL:
G = int(G - (0xff / WS2812_LED_LEVEL))
R = int(R + (0xff / WS2812_LED_LEVEL))
phase += 1
if phase == WS2812_LED_LEVEL:
G = 0
R = 0xff
elif phase < (WS2812_LED_LEVEL * 2):
R = int(R - (0xff / WS2812_LED_LEVEL))
B = int(B + (0xff / WS2812_LED_LEVEL))
phase += 1
if phase == (WS2812_LED_LEVEL * 2):
R = 0
B = 0xff
elif phase < (WS2812_LED_LEVEL * 3):
B = int(B - (0xff / WS2812_LED_LEVEL))
G = int(G + (0xff / WS2812_LED_LEVEL))
phase += 1
if phase == (WS2812_LED_LEVEL * 3):
phase = 0
B = 0
G = 0xff
np[0] = (G,R,B)
return phase
if __name__ == "__main__":
# pin = Pin(36, Pin.OUT)
p9 = Pin(9, Pin.IN) #配置GPIO9为输入端口
WS2812_POWERON = 1
WS2812_POWEROFF = 0
ws2812_status = WS2812_POWEROFF
offset = 0
wlan = esp32_wifi("STA_MODE")
# wlan.configwifi("weald2000","fkepln_730401")
# wlan.save_wificonfig()
while True:
if wlan.check_wifi():
wlan.periodic_time_sync(3600)
# if wlan.sync_time_with_timezone():
# print("当前时间:",wlan.format_time())
if (p9.value() == 0): #是否有按键?有,切换RGB灯的状态
ws2812_status ^= 1
while True:
if (p9.value() == 1): #等待按键释放
break
datetimetxt = wlan.format_date()
oled.Set_gb_font_name("GB18030P.BIN")
oled.fill(0x0)
oled.rect(0,0,128,64,1)
oled.show_chinese("日期",3,3,1)
oled.text(datetimetxt,40,8,1)
oled.show_chinese("时间",3,23,1)
datetimetxt = wlan.format_time()
oled.text(datetimetxt,40,28,1)
wendu = aht20_sensor.temperature
shidu = aht20_sensor.relative_humidity
oled.show_chinese("温",3,43,1)
oled.text("{:>4.1f}".format(wendu),23,48,1)
oled.show_chinese("湿",64,43,1)
oled.text("{:>4.1f}".format(shidu),84,48,1)
# oled.show_chinese("深圳源悦科技",3,3,1)
oled.show()
time.sleep_ms(100)
完整的代码请参考:MicroPythonforesp32wifi配置,时间同步,获取天气信息并显示资源-优快云文库
通过本文的实践,您已掌握ESP32-C3的无线连接和时间同步核心技术。
ESP32-C3的WIFI连接的不仅仅是网络,更是通向智能世界星光大道——让我们用MicroPython在这条大道上狂飙!