用Python在树莓派Zero 2W的OLED屏上显示天气和新闻(Adafruit SSD1306库实战)
用Python在树莓派Zero 2W的OLED屏上显示天气和新闻Adafruit SSD1306库实战树莓派Zero 2W以其小巧的体积和强大的功能成为创客们的最爱。而0.96英寸OLED屏幕则因其高对比度和低功耗特性成为显示信息的理想选择。本文将带你从零开始在树莓派Zero 2W上使用Python和Adafruit SSD1306库打造一个能够显示系统状态、天气预报和新闻头条的智能信息中心。1. 硬件准备与环境配置1.1 硬件连接注意事项在开始之前确保你已经正确连接了OLED屏幕和树莓派Zero 2W。以下是关键连接点树莓派引脚OLED屏幕引脚说明3.3VVCC电源正极GNDGND地线GPIO2 (SDA)SDAI2C数据线GPIO3 (SCL)SCLI2C时钟线注意务必在树莓派断电状态下进行连接操作带电插拔GPIO可能导致硬件损坏。1.2 启用I2C接口连接好硬件后需要启用树莓派的I2C接口sudo raspi-config在配置界面中选择Interface Options选择I2C选择Yes启用重启树莓派使设置生效验证I2C是否正常工作sudo i2cdetect -y 1如果看到OLED屏幕的I2C地址通常是0x3C说明连接成功。1.3 安装必要软件包安装运行所需的依赖库sudo apt-get update sudo apt-get install -y python3-pip python3-pil python3-numpy sudo pip3 install Adafruit-SSD1306 requests beautifulsoup42. 基础显示功能实现2.1 初始化OLED屏幕创建一个基础显示脚本oled_display.pyimport time import Adafruit_SSD1306 from PIL import Image, ImageDraw, ImageFont # 初始化128x64像素的OLED屏幕 disp Adafruit_SSD1306.SSD1306_128_64(rstNone, i2c_address0x3C) # 开始显示 disp.begin() disp.clear() disp.display() # 创建空白图像用于绘制 width disp.width height disp.height image Image.new(1, (width, height)) draw ImageDraw.Draw(image) # 加载字体 font ImageFont.load_default() # 绘制文本 draw.text((0, 0), Hello, OLED!, fontfont, fill255) # 显示图像 disp.image(image) disp.display()2.2 显示系统状态信息扩展脚本以显示树莓派的系统状态import subprocess def get_cpu_temp(): temp subprocess.getoutput(vcgencmd measure_temp) return temp.split()[1].split()[0] def get_mem_usage(): mem subprocess.getoutput(free -m).splitlines()[1].split() return f{mem[2]}/{mem[1]}MB while True: # 清空画布 draw.rectangle((0, 0, width, height), outline0, fill0) # 显示系统信息 draw.text((0, 0), fCPU Temp: {get_cpu_temp()}C, fontfont, fill255) draw.text((0, 16), fMemory: {get_mem_usage()}, fontfont, fill255) draw.text((0, 32), fIP: {subprocess.getoutput(hostname -I)}, fontfont, fill255) # 更新显示 disp.image(image) disp.display() time.sleep(5)3. 获取并显示天气信息3.1 使用天气API注册一个免费的天气API服务如OpenWeatherMap获取API密钥后可以这样获取天气数据import requests import json def get_weather(api_key, city): url fhttp://api.openweathermap.org/data/2.5/weather?q{city}appid{api_key}unitsmetric response requests.get(url) data json.loads(response.text) return { temp: data[main][temp], desc: data[weather][0][description], humidity: data[main][humidity] }3.2 在OLED上显示天气将天气信息整合到显示脚本中weather get_weather(YOUR_API_KEY, Beijing) draw.text((0, 48), fWeather: {weather[temp]}C, fontfont, fill255) draw.text((0, 56), f{weather[desc]}, fontfont, fill255)4. 获取并滚动显示新闻头条4.1 使用BeautifulSoup抓取新闻from bs4 import BeautifulSoup def get_news(): url https://news.google.com response requests.get(url) soup BeautifulSoup(response.text, html.parser) headlines [h.text for h in soup.find_all(h3)[:5]] return headlines4.2 实现滚动文本效果创建一个函数来实现文本的平滑滚动效果def scroll_text(text, y_pos): for i in range(len(text)*6 width): draw.rectangle((0, y_pos, width, y_pos8), outline0, fill0) draw.text((-i, y_pos), text, fontfont, fill255) disp.image(image) disp.display() time.sleep(0.05)4.3 整合新闻显示在主循环中添加新闻显示逻辑news get_news() for headline in news: scroll_text(headline, 40) time.sleep(1)5. 优化与高级功能5.1 多页面切换使用按钮实现多页面切换功能。首先连接一个按钮到GPIOimport RPi.GPIO as GPIO GPIO.setmode(GPIO.BCM) BUTTON_PIN 17 GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_downGPIO.PUD_UP) current_page 0 def button_callback(channel): global current_page current_page (current_page 1) % 3 GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callbackbutton_callback, bouncetime200)然后在主循环中根据current_page显示不同内容。5.2 使用自定义字体安装并使用更大的字体提高可读性sudo apt-get install fonts-dejavu然后在Python代码中font_large ImageFont.truetype(DejaVuSans.ttf, 16)5.3 自动亮度调节根据环境光线自动调节屏幕亮度from gpiozero import LightSensor ldr LightSensor(4) def update_brightness(): contrast int(ldr.value * 255) disp.set_contrast(contrast)在主循环中调用这个函数即可实现自动亮度调节。6. 项目整合与部署6.1 创建完整的显示脚本将所有功能整合到一个脚本中import time import Adafruit_SSD1306 from PIL import Image, ImageDraw, ImageFont import subprocess import requests import json from bs4 import BeautifulSoup import RPi.GPIO as GPIO from gpiozero import LightSensor # 初始化硬件 disp Adafruit_SSD1306.SSD1306_128_64(rstNone, i2c_address0x3C) disp.begin() disp.clear() disp.display() # 设置按钮 GPIO.setmode(GPIO.BCM) BUTTON_PIN 17 GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_downGPIO.PUD_UP) # 设置光敏电阻 ldr LightSensor(4) # 创建图像对象 width disp.width height disp.height image Image.new(1, (width, height)) draw ImageDraw.Draw(image) # 加载字体 font_small ImageFont.load_default() font_large ImageFont.truetype(DejaVuSans.ttf, 16) # 状态变量 current_page 0 last_update 0 news_headlines [] weather_data {} def get_system_info(): # 获取CPU温度 temp subprocess.getoutput(vcgencmd measure_temp) cpu_temp temp.split()[1].split()[0] # 获取内存使用 mem subprocess.getoutput(free -m).splitlines()[1].split() mem_usage f{mem[2]}/{mem[1]}MB # 获取IP地址 ip subprocess.getoutput(hostname -I) return { cpu_temp: cpu_temp, mem_usage: mem_usage, ip: ip } def get_weather(): # 实现天气获取逻辑 pass def get_news(): # 实现新闻获取逻辑 pass def scroll_text(text, y_pos): # 实现滚动文本 pass def button_callback(channel): global current_page current_page (current_page 1) % 3 GPIO.add_event_detect(BUTTON_PIN, GPIO.FALLING, callbackbutton_callback, bouncetime200) try: while True: # 更新亮度 contrast int(ldr.value * 255) disp.set_contrast(contrast) # 每小时更新一次天气和新闻 if time.time() - last_update 3600: weather_data get_weather() news_headlines get_news() last_update time.time() # 清空画布 draw.rectangle((0, 0, width, height), outline0, fill0) # 根据当前页面显示不同内容 if current_page 0: # 显示系统信息 info get_system_info() draw.text((0, 0), fCPU: {info[cpu_temp]}C, fontfont_small, fill255) draw.text((0, 16), fMem: {info[mem_usage]}, fontfont_small, fill255) draw.text((0, 32), fIP: {info[ip]}, fontfont_small, fill255) elif current_page 1: # 显示天气信息 draw.text((0, 0), Weather Info, fontfont_large, fill255) draw.text((0, 20), fTemp: {weather_data[temp]}C, fontfont_small, fill255) draw.text((0, 36), fHumidity: {weather_data[humidity]}%, fontfont_small, fill255) draw.text((0, 52), weather_data[desc], fontfont_small, fill255) elif current_page 2: # 显示新闻 draw.text((0, 0), Latest News, fontfont_large, fill255) for i, headline in enumerate(news_headlines[:3]): scroll_text(headline, 20 i*15) # 更新显示 disp.image(image) disp.display() time.sleep(0.1) except KeyboardInterrupt: GPIO.cleanup() disp.clear() disp.display()6.2 设置开机自启动为了让脚本在树莓派启动时自动运行创建一个systemd服务sudo nano /etc/systemd/system/oled_display.service添加以下内容[Unit] DescriptionOLED Display Service Afternetwork.target [Service] ExecStart/usr/bin/python3 /home/pi/oled_display.py WorkingDirectory/home/pi StandardOutputinherit StandardErrorinherit Restartalways Userpi [Install] WantedBymulti-user.target然后启用并启动服务sudo systemctl enable oled_display.service sudo systemctl start oled_display.service6.3 外壳设计与保护考虑为你的项目设计一个3D打印外壳或使用现成的保护盒确保树莓派和OLED屏幕的安全。可以在外壳上开孔以便按钮操作和光线感应。