跳到主要内容

网络天气显示

重要提示:关于开发板的兼容性

本教程的核心逻辑适用于所有 ESP32 开发板,但所有操作步骤均以 微雪 ESP32-S3-Zero 迷你开发板 为例进行讲解。如果您使用其他型号的开发板,请根据实际情况修改相应设置。

项目介绍

本项目将展示如何利用 ESP32 制作一个网络天气显示器。通过连接 Wi-Fi 网络,ESP32 将定期从 心知天气 API 获取指定城市的实时天气数据(天气现象和温度),并将这些信息显示在微雪 1.5 寸 OLED 屏幕上。

硬件连接

需要使用的器件有:

按照下面接线图连接电路:

ESP32-S3-Zero 引脚图

ESP32-S3-Zero-Pinout

提示

以下用 SPI 接口连接 OLED 显示屏,此屏幕也支持 I2C,通过 BS1 和 BS2 控制,如果使用 I2C 模式,请参考 第7节 I2C 通信 中的接线方式。

ESP32 引脚OLED 模块说明
GPIO 13SCKSPI 时钟线
GPIO 11MOSISPI 数据输出
GPIO 10CS片选信号
GPIO 8DC数据/命令选择
3.3VVCC电源正极
GNDGND电源负极
接线图

代码实现

提示

此代码示例依赖 ssd1327.py 驱动库。该库基于社区开发者 mcauser 的 micropython-ssd1327 项目。

下载链接:micropython-ssd1327-master.zip

请将该库中的 ssd1327.py 文件上传到开发板的根目录中。

import time
import network
import urequests
import json
from machine import Pin, SPI
import ssd1327

# Wi-Fi 配置
WIFI_SSID = "Maker"
WIFI_PASSWORD = "12345678"

# 心知天气 API 配置 (请替换为你的私钥)
API_KEY = "your_api_key"
LOCATION = "shenzhen" # 你想查询天气的城市

# API URL
API_URL = "https://api.seniverse.com/v3/weather/now.json?key={}&location={}&language=en&unit=c"

# 更新间隔 (秒)
UPDATE_INTERVAL = 1800 # 30 分钟

# SPI 引脚配置
SCK_PIN = 13
MOSI_PIN = 11
CS_PIN = 10
DC_PIN = 8
RST_PIN = 9
# 初始化硬件 SPI,使用 id=1,设置时钟频率为 10 MHz
spi = SPI(1, baudrate=10000000, sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN))

# 若使用 I2C 接口,取消注释以下代码
# from machine import I2C
# SDA_PIN = 2
# SCL_PIN = 1
# I2C_ADDR = 0x3d
# i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)

# 初始化显示器
try:
# 使用 SPI 接口
oled = ssd1327.SSD1327_SPI(128, 128, spi, dc=Pin(DC_PIN), res=Pin(RST_PIN), cs=Pin(CS_PIN))

# 使用 I2C 接口
# oled = ssd1327.SSD1327_I2C(128, 128, i2c, I2C_ADDR)

print("OLED display initialized successfully.")
except Exception as e:
print(f"Error initializing display: {e}")
# 如果显示器初始化失败,程序无法继续
while True:
time.sleep(1)

def connect_wifi():
"""连接到 Wi-Fi 网络"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print(f"Connecting to network: {WIFI_SSID}...")
oled.fill(0)
oled.text("Connecting to", 5, 20, 15)
oled.text("WiFi...", 5, 40, 15)
oled.show()

wlan.connect(WIFI_SSID, WIFI_PASSWORD)

# 等待连接成功
timeout = 15 # 15 秒超时
start_time = time.time()
while not wlan.isconnected() and (time.time() - start_time) < timeout:
time.sleep(1)
print(".", end="")

if wlan.isconnected():
print("\nNetwork connected!")
print(f"IP Address: {wlan.ifconfig()[0]}")
oled.fill(0)
oled.text("WiFi Connected!", 5, 20, 15)
oled.text("IP:", 5, 40, 15)
oled.text(wlan.ifconfig()[0], 5, 55, 15)
oled.show()
time.sleep(2)
return True
else:
print("\nFailed to connect to WiFi.")
oled.fill(0)
oled.text("WiFi Failed!", 5, 20, 15)
oled.show()
return False

def get_weather():
"""从心知天气 API 获取天气数据"""
url = API_URL.format(API_KEY, LOCATION)
print(f"Fetching weather from: {url}")

try:
response = urequests.get(url)

if response.status_code == 200:
weather_data = response.json()
print("API Response:", weather_data) # 调试时可以取消注释

# 从 JSON 数据中提取所需信息
result = weather_data['results'][0]
location_name = result['location']['name']
weather_text = result['now']['text']
temperature = result['now']['temperature']

return location_name, weather_text, temperature

else:
print(f"Error getting weather: HTTP Status {response.status_code}")
return None, f"HTTP Err {response.status_code}", ""

except Exception as e:
print(f"Error during API request: {e}")
return None, "Request Error", ""

def display_weather(city, weather, temp):
"""在 OLED 上显示天气信息"""
oled.fill(0) # 清屏

# 城市名称
oled.text(f"City: {city}", 5, 10, 15)

# 天气状况
oled.text(f"Weather:", 5, 40, 15)
oled.text(weather, 5, 55, 15)

# 温度
oled.text(f"Temp: {temp} C", 5, 85, 15)

oled.show() # 更新显示
print(f"Display updated: {city}, {weather}, {temp} C")

#主程序
def main():
# 首先连接 Wi-Fi
if not connect_wifi():
# 如果连接失败,则不再继续
return

while True:
print("\n" + "="*20)
oled.fill(0)
oled.text("Fetching...", 5, 20, 15)
oled.show()

city, weather, temp = get_weather()

if city:
display_weather(city, weather, temp)
else:
# 如果获取失败,显示错误信息
display_weather("Error", weather, temp)

print(f"Waiting for {UPDATE_INTERVAL} seconds before next update...")
time.sleep(UPDATE_INTERVAL)

# 运行主程序
if __name__ == "__main__":
main()

代码解释

  • 导入库

    • network:用于管理 Wi-Fi 连接。
    • urequests:用于发送 HTTP 请求,从 API 获取数据。
    • json:用于解析 API 返回的 JSON 格式数据。
    • machine:用于控制硬件(SPI 和 GPIO)。
    • ssd1327:用于驱动 1.5 寸 OLED 显示屏。
    • time:用于实现延时和计时。
  • 配置参数:程序开头定义了 Wi-Fi 信息、API 密钥、目标城市以及硬件引脚。将这些参数集中管理,方便用户修改配置。

    重要提示:关于心知天气 API

    本项目使用了心知天气 API,需要在 心知天气 注册账号(注册后可选择免费计划),并在 控制台 获取 API 密钥,将私钥添加到 API_KEY 中。

    # Wi-Fi 配置
    WIFI_SSID = "Maker"
    WIFI_PASSWORD = "12345678"

    # 心知天气 API 配置
    API_KEY = "your_api_key"
    LOCATION = "shenzhen"
  • 硬件初始化

    • SPI 方式(默认):使用 machine.SPI() 初始化 SPI 总线,并使用 ssd1327.SSD1327_SPI() 初始化 OLED 显示屏。

      spi = SPI(1, baudrate=10000000, sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN))
      oled = ssd1327.SSD1327_SPI(128, 128, spi, dc=Pin(DC_PIN), res=Pin(RST_PIN), cs=Pin(CS_PIN))
    • I2C 方式:如果使用 I2C 接口的屏幕,可以取消相关代码的注释。使用 machine.I2C() 初始化 I2C 总线,并使用 ssd1327.SSD1327_I2C() 初始化。

      # I2C 初始化示例
      i2c = I2C(0, scl=Pin(SCL_PIN), sda=Pin(SDA_PIN), freq=400000)
      oled = ssd1327.SSD1327_I2C(128, 128, i2c, I2C_ADDR)
  • 网络连接函数 connect_wifi():负责连接 Wi-Fi 网络并在屏幕上显示连接状态。

    • 使用 network.WLAN(network.STA_IF) 创建站点接口。
    • 调用 wlan.connect() 发起连接。
    • 使用 while 循环等待连接成功,并设置了 15 秒的超时机制。
    • 连接过程中会在 OLED 上显示 "Connecting...",成功后显示 IP 地址。
  • 获取天气函数 get_weather():从心知天气 API 获取数据。使用心知天气 API 的天气实况接口,获取指定城市的当前天气信息。接口文档:心知天气 API - 天气实况接口

    • 使用 API_URL.format() 构建完整的请求 URL。

      API_URL = "https://api.seniverse.com/v3/weather/now.json?key={}&location={}&language=en&unit=c"
      ...
      url = API_URL.format(API_KEY, LOCATION)
    • 使用 urequests.get(url) 发送 HTTP GET 请求。

    • 检查 HTTP 状态码是否为 200(成功)。

    • 使用 response.json() 解析返回的数据,并提取城市名、天气现象和温度。

      # 从 JSON 数据中提取所需信息
      result = weather_data['results'][0]
      location_name = result['location']['name']
      weather_text = result['now']['text']
      temperature = result['now']['temperature']
  • 显示函数 display_weather():将获取到的天气信息显示在 OLED 屏幕上。

    • oled.fill(0):清除屏幕。
    • oled.text():分别在不同位置显示城市、天气和温度。
    • oled.show():刷新屏幕显示。
  • 主程序逻辑 main()

    • 首先调用 connect_wifi() 确保网络已连接。
    • 进入无限循环 while True
      • 显示 "Fetching..." 提示正在获取数据。
      • 调用 get_weather() 获取最新天气。
      • 调用 display_weather() 更新屏幕显示。
      • 使用 time.sleep(UPDATE_INTERVAL) 进入休眠,等待下一次更新(默认 30 分钟)。

参考链接