ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

树莓派复古气象站:GPIO控制与Python物联网开发实践

2026/8/20 2:40:02 拓冰建站 浏览量
树莓派复古气象站:GPIO控制与Python物联网开发实践 1. 项目概述一个复古风格的树莓派气象站“1984 WeatherMan Pi”这个项目听起来就带着一股浓浓的复古科技感。它本质上是一个基于树莓派Raspberry Pi的桌面级气象站但它的灵魂在于其呈现方式——模拟上世纪80年代或者说乔治·奥威尔笔下的“1984”那种老式终端或监视器的显示风格。这不仅仅是一个技术项目更是一次软硬件结合的数字艺术创作。它的核心功能是实时获取天气数据并通过树莓派的GPIO接口连接一些简单的传感器比如温湿度传感器DHT11/DHT22或者更酷一点通过调用互联网上的天气API比如OpenWeatherMap来获取更全面的信息。然后它不会把这些数据以现代华丽的网页或APP图表展示而是通过一个运行在树莓派上的Python程序在一个小小的屏幕上比如3.5寸的LCD屏或者干脆就是SSH终端以单色、低分辨率、闪烁光标、ASCII字符甚至伪图形的方式呈现出来营造出一种穿越回早期计算机时代的观感。这个项目适合谁呢首先肯定是树莓派和Python的爱好者尤其是那些对“复古计算”Retro Computing和“赛博朋克”美学感兴趣的人。它不需要非常深厚的编程功底但需要你愿意动手连接硬件、配置环境、调试代码。通过完成它你不仅能巩固Python编程、API调用、GPIO控制这些物联网IoT开发的核心技能还能获得一个独一无二的、极具个性的桌面摆件。它解决的不是一个刚需问题而是一种情感和审美需求——在万物皆“平滑设计”的今天创造一点带有粗粝感和历史温度的科技产品。2. 核心硬件选型与连接思路要搭建这个气象站硬件是骨架。选型不仅关乎功能实现更直接影响最终的“复古味”。2.1 树莓派型号选择与考量树莓派型号众多从Zero到5代选择哪款是关键的第一步。这里没有唯一答案取决于你的显示方式和性能需求。对于“1984 WeatherMan Pi”我强烈推荐Raspberry Pi 3B 或 Raspberry Pi 4B2GB内存版本。原因如下Pi 3B性能足够驱动一个小型LCD屏并流畅运行Python图形界面如Pygame或终端应用且功耗和发热控制得不错价格也相对适中。Pi 4B则性能更充裕如果你计划未来扩展更多传感器或运行更复杂的后台服务它会游刃有余。不推荐Pi Zero系列除非你打算纯粹以“无头模式”不接显示器仅通过SSH访问运行并通过网络在其他设备上查看终端输出因为Zero的CPU和内存在处理图形和多个Python库时可能会比较吃力。注意如果你手头只有Pi Zero W也不是完全不行。你可以选择运行一个极简的Linux系统如Raspbian Lite然后通过SSH登录在终端里用curses库来制作纯文本界面。这反而可能更贴近“老式绿屏终端”的极致复古感。2.2 传感器与显示单元的选型解析传感器部分本地环境传感器可选但推荐为了体现“物联网”的本地感知能力可以加一个DHT22AM2302温湿度传感器。为什么不选更便宜的DHT11因为DHT22精度更高温度±0.5°C湿度±2-5%量程更广虽然贵一点但数据更可靠。它通过单总线1-Wire协议与树莓派GPIO通信只需要一根数据线连接非常简单。网络数据源核心项目的主要数据很可能来自互联网API。OpenWeatherMap的免费层足够个人使用提供当前天气、预报、以及像体感温度、气压、风速风向等丰富数据。这是呈现专业气象信息的关键。显示部分塑造风格的核心小尺寸LCD屏首选一块3.5寸或5寸的HDMI接口LCD触摸屏是最佳选择。它可以直接插在树莓派的HDMI和GPIO引脚上无需额外驱动通常厂家会提供驱动镜像或脚本。在这样的小屏上显示低分辨率像素风格的界面复古味道正合适。老式CRT显示器或电视终极复古如果你有办法可以通过树莓派的复合视频输出AV口连接到一台真正的老式CRT显示器或带AV输入的旧电视上。那种扫描线的质感是任何现代液晶屏都无法模拟的能将“1984”的氛围拉到最满。不过这需要额外的转接线和一台老设备。纯终端模式不接任何专用显示器只用树莓派本身。你可以通过SSH从你的笔记本电脑或手机连接到树莓派在本地终端里查看Python程序输出的ASCII艺术天气信息。这种方式最极客也最轻量。2.3 GPIO连接图与物理搭建假设我们采用“树莓派4B DHT22 3.5寸LCD屏”的方案。连接示意图如下树莓派 GPIO 引脚示意图顶视图引脚排列 (USB-C电源口在上方) ┌──────────────┐ │ ○ ○ │ │ ○ ○ 1 2 │ - 引脚1 (3.3V) │ ○ ○ 3 4 │ - 引脚4 (5V) │ ○ ○ 5 6 │ │ ○ ○ 7 8 │ │ ○ ○ 9 10 │ │ ○ ○ 11 12 │ │ ○ ○ 13 14 │ │ ○ ○ 15 16 │ │ ○ ○ 17 18 │ │ ○ ○ 19 20 │ │ ○ ○ 21 22 │ │ ○ ○ 23 24 │ │ ○ ○ 25 26 │ │ ○ ○ 27 28 │ │ ○ ○ 29 30 │ │ ○ ○ 31 32 │ │ ○ ○ 33 34 │ │ ○ ○ 35 36 │ │ ○ ○ 37 38 │ │ ○ ○ 39 40 │ └──────────────┘具体连接DHT22VCC引脚1- 树莓派引脚1 (3.3V)。重要必须接3.3V接5V可能会烧毁树莓派或传感器DATA引脚2- 树莓派任意GPIO口例如引脚7 (GPIO4)。我们这里选GPIO4。GND引脚4- 树莓派引脚6 (GND)。DHT22的引脚3通常为空不接3.5寸LCD屏通常通过排线直接插在树莓派顶部的GPIO排针上并占用HDMI通道。请严格按照屏幕附带的说明书安装一般需要先安装驱动。电源为树莓派4B提供一个5V/3A以上的优质电源。供电不足会导致屏幕闪烁、树莓派重启尤其是连接了外设时。物理搭建心得为了让它看起来更像一个“设备”而不是一堆线材可以考虑用一个亚克力外壳或甚至是一个复古的老收音机外壳来容纳树莓派和屏幕。把DHT22传感器用延长线引到外壳外部确保它能感知到环境空气而不是设备内部的热量。3. 软件环境配置与核心库详解硬件连接好后我们需要为树莓派注入灵魂——软件。这个过程是项目成功的基础很多坑都发生在这里。3.1 操作系统选择与基础配置首先为你的树莓派烧录一个操作系统。我推荐使用Raspberry Pi OS (Legacy) with desktop的32位版本。虽然64位是趋势但一些旧的库和教程对32位兼容性更好且对于这个项目性能完全过剩。使用Raspberry Pi Imager工具烧录时记得在设置中按CtrlShiftX提前启用SSH、设置Wi-Fi和国家、配置用户名密码。这样烧录好的SD卡一开机就能自动联网你可以直接通过SSH连接无需接键盘鼠标显示器这就是所谓的“无头模式”启动非常方便。首次启动并通过SSH登录后第一件事是更新系统sudo apt update sudo apt full-upgrade -y sudo reboot3.2 Python环境与关键库安装树莓派OS默认安装了Python 3。我们需要安装项目所需的Python库。这里会用到pipPython包管理器。确保pip是最新版sudo apt install python3-pip -y pip3 install --upgrade pip安装硬件操作库RPi.GPIO最经典的树莓派GPIO控制库。虽然有点老但简单直接。pip3 install RPi.GPIOAdafruit_DHT这是读取DHT系列传感器最稳定、最常用的库之一。Adafruit社区提供了优秀的支持和文档。sudo apt install python3-dev python3-pip sudo apt install libgpiod2 pip3 install Adafruit_DHT注意在较新的树莓派OS上使用libgpiod2是更现代的方式Adafruit_DHT的新版本可能依赖它。安装网络请求与数据处理库requests用于调用天气API简单易用。pip3 install requestspython-dotenv强烈推荐用于管理你的API密钥等敏感信息避免将它们硬编码在脚本里。pip3 install python-dotenv安装图形显示库取决于你的显示方案方案APygame用于LCD屏图形化显示功能强大可以绘制像素风格的图形、文字和动画。sudo apt install python3-pygame方案Bcurses用于纯终端文本显示Python标准库自带无需安装。专门用于在终端中创建文本用户界面(TUI)可以控制光标、颜色、创建窗口等完美契合复古终端主题。方案CPIL/Pillow用于生成图像可结合Pygame如果需要加载或处理一些复古字体位图会用到。pip3 install Pillow3.3 API密钥申请与环境变量配置以OpenWeatherMap为例去其官网注册免费账户在控制面板找到你的API Key。接下来永远不要把这个Key写在代码里在项目目录下创建一个名为.env的文件nano .env内容如下OWM_API_KEY你的_32位_API密钥_在这里 CITY_NAMEBeijing,CN LAT39.9042 LON116.4074这里除了API密钥还定义了城市名和经纬度。你可以用城市名也可以用更精确的经纬度。然后在Python脚本中这样安全地使用它import os from dotenv import load_dotenv import requests load_dotenv() # 加载 .env 文件中的变量 api_key os.getenv(OWM_API_KEY) city os.getenv(CITY_NAME) lat os.getenv(LAT) lon os.getenv(LON) url fhttp://api.openweathermap.org/data/2.5/weather?q{city}appid{api_key}unitsmetric # 或者用经纬度fhttp://...weather?lat{lat}lon{lon}appid... response requests.get(url) data response.json()这样做的好处是当你把代码分享到GitHub时把.env文件加入.gitignore你的密钥就永远不会泄露。4. 核心代码实现与复古UI设计这是项目的核心创意部分。我们将分别实现数据获取和两种风格的显示。4.1 数据获取模块本地传感器与网络API融合我们创建一个weather_data.py模块负责统一获取所有数据。# weather_data.py import time import Adafruit_DHT import requests import os from dotenv import load_dotenv load_dotenv() class WeatherFetcher: def __init__(self): self.dht_sensor Adafruit_DHT.DHT22 self.dht_pin 4 # GPIO4 对应物理引脚7 self.api_key os.getenv(OWM_API_KEY) self.city os.getenv(CITY_NAME) def read_local_sensor(self): 读取本地DHT22传感器数据 humidity, temperature Adafruit_DHT.read_retry(self.dht_sensor, self.dht_pin) # read_retry会尝试多次避免单次读取失败 if humidity is not None and temperature is not None: return {local_temp: round(temperature, 1), local_humidity: round(humidity, 1)} else: print(Failed to read from DHT22 sensor!) return {local_temp: None, local_humidity: None} def fetch_online_weather(self): 从OpenWeatherMap获取在线天气数据 try: url fhttp://api.openweathermap.org/data/2.5/weather?q{self.city}appid{self.api_key}unitsmetriclangzh_cn response requests.get(url, timeout10) # 设置超时 data response.json() if response.status_code 200: main data[main] weather data[weather][0] wind data[wind] return { online_temp: main[temp], feels_like: main[feels_like], pressure: main[pressure], humidity: main[humidity], description: weather[description], icon: weather[icon], # 用于获取天气图标 wind_speed: wind[speed], wind_deg: wind[deg] } else: print(fAPI Error: {data.get(message, Unknown)}) return None except requests.exceptions.RequestException as e: print(fNetwork error: {e}) return None def get_all_data(self): 获取所有数据返回一个字典 all_data {} local_data self.read_local_sensor() online_data self.fetch_online_weather() all_data.update(local_data) if online_data: all_data.update(online_data) all_data[fetch_time] time.strftime(%H:%M:%S) return all_data if __name__ __main__: fetcher WeatherFetcher() print(fetcher.get_all_data())这个类封装得很好主程序只需要调用get_all_data()就能拿到融合后的数据字典。4.2 复古终端界面Curses实现如果你选择纯终端显示curses库是你的不二之选。它能让你的终端看起来像老式的VT100终端。# display_curses.py import curses import time from weather_data import WeatherFetcher def draw_weather(stdscr, data): 在curses窗口中绘制天气信息 stdscr.clear() height, width stdscr.getmaxyx() # 1. 绘制一个复古边框 stdscr.border(0) # 2. 标题 - 使用反色高亮 title [ 1984 WEATHERMAN PI v1.0 ] stdscr.addstr(1, (width - len(title)) // 2, title, curses.A_REVERSE) # 3. 显示数据 # 模拟老式终端打字机效果 info_lines [ fSYSTEM TIME: {data.get(fetch_time, N/A)}, --- LOCAL SENSOR ---, fTEMP: {data.get(local_temp, --.-):5} °C, fHUMI: {data.get(local_humidity, --):5} %, --- ONLINE DATA ---, fCONDITION: {data.get(description, N/A).upper()}, fTEMP: {data.get(online_temp, --.-):5} °C, fFEELS LIKE: {data.get(feels_like, --.-):5} °C, fPRESSURE: {data.get(pressure, ---):5} hPa, fWIND: {data.get(wind_speed, --.-):4} m/s, , PRESS q TO EXIT ] start_line 4 for i, line in enumerate(info_lines): try: # 逐字符打印模拟打字效果 for j, ch in enumerate(line): stdscr.addch(start_line i, 10 j, ch) stdscr.refresh() time.sleep(0.02) # 控制打字速度 stdscr.addch(start_line i, 10 len(line), \n) except curses.error: pass # 忽略窗口边缘绘制错误 # 4. 在底部创建一个状态栏 status STATUS: DATA SYNC OK stdscr.addstr(height-2, 2, status, curses.A_DIM) def main(stdscr): # 初始化curses curses.curs_set(0) # 隐藏光标 stdscr.nodelay(1) # 使getch非阻塞 stdscr.timeout(1000) # 设置getch超时毫秒用于定时刷新 fetcher WeatherFetcher() data {} while True: # 获取新数据例如每10次循环获取一次避免频繁调用API # 这里简化处理每次循环都获取。实际应加计时器。 data fetcher.get_all_data() # 绘制界面 draw_weather(stdscr, data) # 检查按键输入 key stdscr.getch() if key ord(q) or key ord(Q): break # 短暂停顿控制刷新率 time.sleep(0.1) if __name__ __main__: curses.wrapper(main)运行这个脚本(python3 display_curses.py)你会在终端里看到一个带有边框、反色标题、模拟打字机效果和状态栏的复古天气界面。按q退出。4.3 像素图形界面Pygame实现对于LCD屏我们可以用Pygame创造更丰富的像素视觉。# display_pygame.py import pygame import sys import time from weather_data import WeatherFetcher # 初始化 pygame.init() # 设置屏幕大小匹配你的LCD屏分辨率例如480x320 screen pygame.display.set_mode((480, 320)) pygame.display.set_caption(1984 WeatherMan Pi) clock pygame.time.Clock() # 定义复古配色 - 经典琥珀色CRT风格 COLOR_BG (10, 8, 0) # 深棕黑背景 COLOR_TEXT (255, 176, 0) # 琥珀色文字 COLOR_GLOW (255, 200, 50, 100) # 带透明度的辉光色 COLOR_SCANLINE (0, 0, 0, 60) # 扫描线效果 # 加载一个像素字体或者使用系统自带字体并缩小 try: font_large pygame.font.Font(assets/retro_font.ttf, 32) # 假设你有字体文件 font_medium pygame.font.Font(assets/retro_font.ttf, 24) font_small pygame.font.Font(assets/retro_font.ttf, 16) except: # 备用字体 font_large pygame.font.SysFont(couriernew, 32, boldTrue) font_medium pygame.font.SysFont(couriernew, 24) font_small pygame.font.SysFont(couriernew, 16) fetcher WeatherFetcher() last_fetch_time 0 fetch_interval 60 # 每60秒获取一次新数据 data {} def draw_scanlines(surface): 绘制CRT扫描线效果 scanline pygame.Surface((surface.get_width(), 2), pygame.SRCALPHA) scanline.fill(COLOR_SCANLINE) for y in range(0, surface.get_height(), 4): # 每4像素一条扫描线 surface.blit(scanline, (0, y)) def render_text_with_glow(font, text, color, glow_color, x, y, surface): 渲染带辉光效果的文字模拟CRT辉光 # 先渲染一个稍大、模糊的版本作为辉光 glow_surf font.render(text, True, glow_color) # 这里简化处理实际可以用高斯模糊。我们简单多次绘制制造光晕。 for offset in [(1,1), (-1,1), (1,-1), (-1,-1)]: surface.blit(glow_surf, (xoffset[0], yoffset[1])) # 再渲染清晰的文字主体 text_surf font.render(text, True, color) surface.blit(text_surf, (x, y)) def draw(surface): surface.fill(COLOR_BG) # 绘制标题 render_text_with_glow(font_large, WEATHER SURVEILLANCE, COLOR_TEXT, COLOR_GLOW, 20, 10, surface) # 绘制分隔线 pygame.draw.line(surface, COLOR_TEXT, (20, 60), (460, 60), 2) # 显示数据 if data: y_offset 80 # 本地数据 local_temp data.get(local_temp, --.-) local_humi data.get(local_humidity, --) text font_medium.render(fLOCAL: {local_temp}°C {local_humi}% RH, True, COLOR_TEXT) surface.blit(text, (30, y_offset)) # 在线数据 y_offset 50 desc data.get(description, N/A).upper() online_temp data.get(online_temp, --.-) feels data.get(feels_like, --.-) text font_medium.render(fZONE: {desc}, True, COLOR_TEXT) surface.blit(text, (30, y_offset)) y_offset 40 text font_small.render(fTEMP: {online_temp}°C (FEELS: {feels}°C), True, COLOR_TEXT) surface.blit(text, (50, y_offset)) y_offset 30 wind data.get(wind_speed, --.-) text font_small.render(fWIND: {wind} m/s, True, COLOR_TEXT) surface.blit(text, (50, y_offset)) # 绘制底部状态栏和时间 pygame.draw.rect(surface, (30, 25, 0), (0, 280, 480, 40)) status_time time.strftime(%H:%M:%S) time_text font_small.render(fSYNC: {data.get(fetch_time, --:--:--)} | SYS: {status_time}, True, COLOR_TEXT) surface.blit(time_text, (20, 290)) # 添加扫描线效果 draw_scanlines(surface) # 主循环 running True while running: current_time time.time() # 定时获取数据 if current_time - last_fetch_time fetch_interval: data fetcher.get_all_data() last_fetch_time current_time print(fData fetched at {data.get(fetch_time)}) # 处理事件 for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if event.key pygame.K_ESCAPE: running False # 绘制界面 draw(screen) pygame.display.flip() clock.tick(30) # 限制帧率 pygame.quit() sys.exit()这个Pygame程序创建了一个深色背景、琥珀色文字、带有扫描线效果的界面充满了老式监视器的感觉。你可以运行它(python3 display_pygame.py)在连接的LCD屏上查看效果。5. 系统集成、优化与自动化让项目从“可运行”变成“好用”还需要最后几步。5.1 开机自启动服务配置我们不希望每次启动树莓派都要手动去运行Python脚本。最好的方式是将其配置为系统服务。创建一个服务文件sudo nano /etc/systemd/system/weatherman.service写入以下内容假设你使用Pygame显示且项目在/home/pi/weatherman目录[Unit] Description1984 WeatherMan Pi Display Service Aftergraphical.target multi-user.target Wantsgraphical.target [Service] Typesimple Userpi EnvironmentDISPLAY:0 EnvironmentXAUTHORITY/home/pi/.Xauthority WorkingDirectory/home/pi/weatherman ExecStart/usr/bin/python3 /home/pi/weatherman/display_pygame.py Restarton-failure RestartSec10 [Install] WantedBygraphical.target关键点解释Aftergraphical.target确保在图形界面启动后再运行我们的程序。EnvironmentDISPLAY:0和XAUTHORITY这两行对于Pygame在图形环境下显示至关重要它告诉程序在哪里绘制窗口。Restarton-failure如果程序崩溃10秒后自动重启提高可靠性。保存退出后启用并启动服务sudo systemctl daemon-reload sudo systemctl enable weatherman.service sudo systemctl start weatherman.service检查服务状态sudo systemctl status weatherman.service。如果看到active (running)恭喜你它已经成功在后台运行了。重启树莓派也会自动启动。实操心得调试服务时如果启动失败可以先用sudo journalctl -u weatherman.service -f查看实时日志或者直接以当前用户身份在终端运行脚本python3 display_pygame.py看具体的错误信息。常见的错误包括路径不对、缺少环境变量特别是DISPLAY、Python库未安装。5.2 性能优化与低功耗运行树莓派一直开着屏幕和程序可能会有点热。我们可以做一些优化屏幕背光控制很多LCD屏的背光可以通过GPIO PWM控制。我们可以在夜间或无人时调暗或关闭背光。这需要查阅你的屏幕规格书找到背光控制引脚。然后可以在Python代码中根据时间控制背光亮度。CPU降频与轻量系统如果使用Pi 4可以在raspi-config中Performance Options-Overclock选择None或保守的频率。或者如果你只用终端显示curses完全可以安装Raspberry Pi OS Lite无桌面版系统负载会低很多。优化数据获取频率天气数据变化没那么快。将API调用和传感器读取间隔设置为5-10分钟是合理的。在代码中做好计时避免每秒都去请求API既浪费资源也可能触发API的调用频率限制。5.3 功能扩展思路基础功能完成后你可以考虑以下扩展让项目更有趣语音播报复古合成音使用pyttsx3或espeak库将天气信息用机械的、复古的合成语音读出来就像老式科幻电影里的电脑。历史数据记录与简单预测使用sqlite3数据库记录每小时的数据。然后可以计算简单的趋势比如“温度正在上升”或“湿度高于昨日同期”并在界面上显示。物理指示灯用几个LED灯连接到GPIO用不同颜色或闪烁模式表示天气状态如蓝色常亮晴绿色闪烁雨红色快速闪烁高温警告。网络状态指示在界面上增加一个简单的“信号强度”图标通过尝试ping网关或访问一个稳定网站来粗略判断网络连接质量。6. 常见问题与深度排查实录在实际操作中你几乎一定会遇到下面这些问题。这里是我踩过坑后的经验总结。6.1 GPIO与传感器读取故障排查问题DHT22读取总是返回None。这是最常见的问题。按照以下步骤排查检查接线最可能确保VCC接的是3.3V引脚1不是5V数据线和地线是否接反或接触不良用万用表测一下引脚1和6之间是否有3.3V电压。检查GPIO编号确认代码中使用的GPIO编号BCM编码与实际物理引脚对应。我们例子中用的GPIO4对应物理引脚7。你可以使用pinout命令查看树莓派引脚图。上拉电阻DHT22的数据线需要一個4.7kΩ - 10kΩ的上拉电阻连接到3.3V以确保信号稳定。很多传感器模块已经内置了这个电阻但如果是单独的传感器你必须自己加一个。电源干扰树莓派的3.3V电源可能带不动多个传感器。尝试单独给DHT22供电仍用树莓派的3.3V和GND或者在其VCC和GND之间加一个0.1uF的陶瓷电容去耦。库与权限确保安装了Adafruit_DHT库。尝试使用sudo运行你的脚本因为早期版本的库可能需要root权限访问GPIO。但更推荐将用户加入gpio组sudo usermod -a -G gpio pi然后注销重新登录。尝试使用libgpiod作为后端。Adafruit_DHT库可能默认使用旧的RPi.GPIO你可以尝试指定sudo apt install python3-libgpiod然后在代码中尝试不同的读取方式但Adafruit_DHT可能不直接支持。问题GPIO接口回读是0但是实际是高电平这通常发生在你试图用软件RPi.GPIO.input(pin)去读取一个被设置为输出模式的引脚的状态。当你设置一个引脚为输出并输出高电平后软件回读的可能只是内部输出寄存器的值而不是物理引脚上真实的电压。如果外部电路有强下拉实际电压可能被拉低但软件回读可能仍是1。反之亦然。解决方案明确引脚模式输入就是输入输出就是输出。不要混用。如果需要读取一个输出引脚的真实外部状态比如连接了开路集电极电路这很困难。更好的设计是使用另一个专门的输入引脚来监测。使用逻辑分析仪或万用表测量物理引脚电压这是判断硬件状态的唯一可靠方法。6.2 网络API调用错误处理问题requests调用API返回错误如400, 401, 429等。400 Bad Request通常是请求参数错误。检查你的API请求URL是否正确城市名格式是否为City,CountryCode如Beijing,CN经纬度是否为数字格式。401 UnauthorizedAPI密钥错误或过期。检查.env文件中的OWM_API_KEY是否正确是否有空格。去OpenWeatherMap网站确认密钥是否激活。429 Too Many Requests免费API有调用频率限制每分钟60次每天1000次等。你的代码可能在一个循环里无节制地调用。务必在代码中添加延时确保调用间隔大于60秒。通用网络错误树莓派网络不稳定。在代码中添加重试机制和超时设置。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session requests.Session() retries Retry(total3, backoff_factor1, status_forcelist[500, 502, 503, 504]) session.mount(http://, HTTPAdapter(max_retriesretries)) session.mount(https://, HTTPAdapter(max_retriesretries)) try: response session.get(url, timeout5) # 设置5秒超时 response.raise_for_status() # 如果状态码不是200抛出异常 data response.json() except requests.exceptions.RequestException as e: print(fRequest failed: {e}) # 使用缓存的上一次数据或显示错误信息6.3 显示与界面相关难题问题Pygame程序在开机自动启动时黑屏或报错。这是服务配置中最常见的问题根本原因通常是图形环境未就绪或显示环境变量未设置。确保服务在图形界面之后启动如前面服务文件所示Aftergraphical.target和Wantsgraphical.target是关键。正确设置环境变量EnvironmentDISPLAY:0和EnvironmentXAUTHORITY/home/pi/.Xauthority必须设置。.Xauthority文件的位置可能因用户而异对于用户pi通常是/home/pi/.Xauthority。以对应用户运行Userpi指定了服务以pi用户身份运行这很重要因为pi用户有图形会话的权限。测试命令在配置服务前先尝试在终端里在图形界面登录的状态下直接运行python3 /home/pi/weatherman/display_pygame.py确保它能正常显示。然后在同一个终端里运行echo $DISPLAY确认输出是:0。再用echo $XAUTHORITY确认路径。查看日志服务启动失败时使用sudo journalctl -u weatherman.service -e查看最新的日志错误信息会给你明确指引。问题Curses界面在SSH终端中显示乱码或错位。终端类型确保你的SSH客户端如PuTTY, Terminal, iTerm2的终端类型TERM设置正确。通常设置为xterm-256color或screen。编码确保终端和树莓派系统使用相同的字符编码通常是UTF-8。窗口大小Curses程序在启动时会获取终端窗口大小。如果你在程序运行后调整了SSH终端窗口大小界面可能会混乱。可以在代码中捕获curses.KEY_RESIZE信号并调用curses.resizeterm()来动态调整但这比较复杂。一个简单办法是固定一个合适的初始窗口大小或者提示用户不要调整窗口。完成以上所有步骤你的“1984 WeatherMan Pi”就应该是一个能够稳定运行、风格独特、充满趣味的桌面气象站了。它静静地立在角落用来自旧时代的目光凝视着当下的风云变幻这本身就是一件很酷的事情。