我有一块(未)吃灰多年的树莓派2和很多块(真)吃灰多年的 Nokia 5110 屏幕,最近收拾了一下,翻出了这块屏幕,然后找出了以前为它写的代码,基本就是网上摘抄的代码。心想不能真的就让它永远吃灰下去,于是今天就重新为它写了代码。
为什么叫高级版呢,因为是以前代码的升级啊。
具体效果如图
前期准备:
- 树莓派: 理论上从一到四代均可。
- Nokia 5110:淘宝几块到十几块不等。
- Linux 基础:基础的不能再基础的基础。
- Python 能力:会打空格就行。如果需要自己定制,那还需要一点基础
要求:
- python 3:2.7已经死了,早点抛弃吧。树莓派自带有python3。
- 知晓如何科学的提问:我不是神,问题不讲清楚怎么帮你解决。
缺陷:
- 不能显示中文:因为我使用的包没有中文字库而且其他能显示中文的字太大了,信息量太少,排版也不好看,就放弃了。
因为是好几年(也就一两年)前的代码。今天去看一个库的时候发现已经停止维护了。也就是不能用 pip 直接安装了(好像一直都不能直接用 pip 安装)。
代码会在最后打包提供下载。
硬件连接
连接方式我直接使用的 https://www.algissalys.com/how-to/nokia-5110-lcd-on-raspberry-pi 的连接方式。一般库用的也是这种连接方式。注意LIGHT口图上是没有标注的,可以连接到3.3V或者5V(但是有些屏幕LIGHT需要接地,具体看淘宝上的描述把)
测试
连接好以后需要测试一下屏幕是否能用或者是否连对了。
在树莓派shell下执行
git clone https://github.com/adafruit/Adafruit_Nokia_LCD
# 先安装这个包
cd Adafruit_Nokia_LCD
sudo python3 setup.py install
# 如果没有问题就进行下一步
cd examples
python3 shapes.py # 还有 image.py和animate.py可以执行,都可以试试
如果执行成功会出现
这个图形,这里我偷懒用的网上的图片。
这时候就可以开始下一步了。
添加代码
我的屏幕主要显示如下几项信息:
时间
天气(使用的和风天气的api)
CPU信息
ram信息
磁盘信息
理论上可以定制无限多的信息,只要你觉得自己看得过来。
主代码(显示代码)
# filename: display.py
# -*- coding: utf-8 -*-
import time
import Adafruit_GPIO.SPI as SPI
import Adafruit_Nokia_LCD as LCD
import pi
import my_weather
import requests
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
from bs4 import BeautifulSoup
get_ip_url = 'http://icanhazip.com/'
get_ip_time: str = str() # 更新ip时间
ip: str = str()
get_weather_time: str = str()
weather: list = list()
max_height = 5 # 屏幕显示的最大行数
sleep_time = 5 # 隔多少秒切换一次屏幕,最低值为1,建议3以上。
# Raspberry Pi hardware SPI config:
DC = 23
RST = 24
SPI_PORT = 0
SPI_DEVICE = 0
def get_my_ip() -> str:
"""
每天更新一次公网IP地址
:return:
"""
global get_ip_time, ip
today_date = time.strftime("%y-%m-%d", time.localtime())
if today_date == get_ip_time or get_ip_time != "":
return str(ip)
else:
get_ip_time = today_date
ip = BeautifulSoup(requests.get(get_ip_url).content, "html.parser", from_encoding='gbk')
return str(ip)
def get_weather() -> list:
"""
每小时更新一次天气情况
:return:
"""
global get_weather_time, weather
now_hour: str = time.strftime("%y-%m-%d %H", time.localtime())
if now_hour == get_weather_time or get_weather_time != "": # 查看天气更新没有
return weather
else:
tmp, pm25, pm10 = my_weather.main() # 温度, pm2.5, pm10
text = 'TEMP:%s' % tmp
pm25_text = 'PM2.5:%s' % pm25
pm10_text = 'PM10:%s' % pm10
current_weather = list([str(text), str(pm25_text), str(pm10_text)])
get_weather_time = now_hour
weather = current_weather
return weather
def split_content(content) -> list:
"""
5110只能显示五行数据,如果超过六行,做分割
:param content:
:return:
"""
temp_list = list()
if len(content) > max_height:
i = 1
while i < len(content):
temp: list = list([content[0]]) # 先把时间append
while len(temp) < max_height and i < len(content):
temp.append(content[i])
i = i + 1
temp_list.append(temp)
else:
temp_list.append(content)
return temp_list
def draw_text(wait_to_draw_content):
# Hardware SPI usage:
disp = LCD.PCD8544(DC, RST, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=4000000))
# Initialize library.
disp.begin(contrast=60)
# Clear display.
disp.clear()
disp.display()
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
image = Image.new('1', (LCD.LCDWIDTH, LCD.LCDHEIGHT))
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
# Draw a white filled box to clear the image.
draw.rectangle((0, 0, LCD.LCDWIDTH, LCD.LCDHEIGHT), outline=255, fill=255)
# Load default font.
font = ImageFont.load_default()
for each in wait_to_draw_content:
text = each
# 根据传进来的字符串进行x轴的确定, 5110宽84 高48, 一个字符宽6个像素点
x_pos = 42 - (len(text) * 6 / 2)
if x_pos < 0:
x_pos = 0
# 一共传进来几个数据就依次从中间开始显示
y_base = int((6 - len(wait_to_draw_content)) / 2)
# y_pos = y_base * 8 + draw_content.index(each) * 8 - 8
y_pos = 8 * (y_base + wait_to_draw_content.index(each))
draw.text((x_pos, y_pos), text, font=font)
# Display image.
disp.image(image)
disp.display()
if __name__ == '__main__':
try:
while True:
now_time = time.strftime("%y-%m-%d %H:%M", time.localtime())
draw_content = [now_time, str(get_my_ip())]
now_weather = get_weather()
draw_content.extend(now_weather)
pi_info: list = pi.main(all=True) # 返回CPU温度和使用率
draw_content.extend(pi_info)
draw_content = split_content(draw_content)
for each in draw_content:
draw_text(each) # 注意消息不要超过六条
time.sleep(sleep_time)
except KeyboardInterrupt as e:
print("exit...")
这是经过我简化的代码,但是所有的必要功能都在。
获取树莓派状态信息
# filename: pi.py
# -*- coding: utf-8 -*-
# 读取树莓派信息
import os
# Return CPU temperature as a character string
def getCPUtemperature():
res = os.popen('/usr/bin/vcgencmd measure_temp').readline()
return res.replace("temp=", "").replace("'C\n", "")
# Return % of CPU used by user as a character string
def getCPUuse():
return str(os.popen(r"/usr/bin/top -n 1 -b| /usr/bin/awk '/Cpu\(s\):/ {print $2}'").readline().strip())
# Return RAM information (unit=kb) in a list
# Index 0: total RAM
# Index 1: used RAM
# Index 2: free RAM
def getRAMinfo():
p = os.popen('free')
i = 0
while 1:
i = i + 1
line = p.readline()
if i == 2:
return line.split()[1:4]
# Return information about disk space as a list (unit included)
# Index 0: total disk space
# Index 1: used disk space
# Index 2: remaining disk space
# Index 3: percentage of disk used
def getDiskSpace():
p = os.popen("df -h /")
i = 0
while 1:
i = i + 1
line = p.readline()
if i == 2:
return line.split()[1:5]
def main(all=False, cpu=True, ram=False, disk=False) -> list:
pi = list()
if all:
cpu = True
ram = True
disk = True
if cpu:
CPU_temp = getCPUtemperature()
CPU_usage = getCPUuse()
cpu_tmp = 'CPU TMP:%s' % CPU_temp
cpu_usg = 'CPU USE:%s' % CPU_usage
# pi = list([str(cpu_tmp), str(cpu_usg)])
pi.append(str(cpu_tmp))
pi.append(str(cpu_usg))
if ram:
RAM_stats = getRAMinfo()
RAM_total = round(int(RAM_stats[0]) / 1000, 1)
RAM_used = round(int(RAM_stats[1]) / 1000, 1)
RAM_free = round(int(RAM_stats[2]) / 1000, 1)
RAM_total = 'RAM|All:' + str(RAM_total)
RAM_used = 'RAM|Used:' + str(RAM_used)
RAM_free = 'RAM|Free:' + str(RAM_free)
pi.append(RAM_total)
pi.append(RAM_used)
pi.append(RAM_free)
if disk:
# Disk information
DISK_stats = getDiskSpace()
DISK_total = DISK_stats[0]
DISK_used = DISK_stats[1]
DISK_perc = DISK_stats[3]
DISK_total = 'DISK|Size:' + str(DISK_total)
DISK_used = 'DISK|Used:' + str(DISK_used)
DISK_perc = 'DISK|Used:' + str(DISK_perc)
pi.append(DISK_total)
pi.append(DISK_used)
pi.append(DISK_perc)
return pi
这个文件主要获取树莓派的CPU、RAM、磁盘的信息。
天气获取
注意:这个文件需要你的 api key,具体可以在 https://console.heweather.com/my/service 申请,同时需要你填入自己的所在地。需要填的项目都用星号表示了
# filename: my_weather.py
# -*- coding: utf-8 -*-
import json
import requests
from bs4 import BeautifulSoup
# ---------基本信息-------------------
# key在网站上获取:https://console.heweather.com/my/service
key = "key=**************" # 填入自己的key
locate = "location=****" # 填入自己的地址,可以具体到区
Regular_weather_data = 'https://free-api.heweather.com/s6/weather?' # 常规天气数据集合
# 根据接口查询
def weather_url(weather_api):
url = weather_api + locate + "&" + key
return url
def get_raw_data(url):
data = requests.get(url)
soup = BeautifulSoup(data.content, "html.parser", from_encoding='utf-8')
soup = str(soup)
json_data = json.loads(soup)
return json_data
def LiveWeather(data):
now_cond = data['HeWeather6'][0]['now']['cond_txt'] # 天气:阴,晴
Sensible_temperature = data['HeWeather6'][0]['now']['fl'] # 体感温度
tmp = data['HeWeather6'][0]['now']['tmp'] # 温度
vis = data['HeWeather6'][0]['now']['vis'] # 能见度
update_time = data['HeWeather6'][0]['update']['loc'] # 更新时间
dress_suggestion_brf = data['HeWeather6'][0]['lifestyle'][1]['brf'] # 生活指数简介
dress_suggestion_description = data['HeWeather6'][0]['lifestyle'][1]['txt'] # 生活指数详细描述
return tmp
def air_quality(data):
aqi = data['HeWeather6'][0]['air_now_station'][0]['aqi'] # 空气质量指数
qlty = data['HeWeather6'][0]['air_now_station'][0]['qlty'] # 空气质量
co = data['HeWeather6'][0]['air_now_station'][0]['co'] # CO
pm25 = data['HeWeather6'][0]['air_now_station'][0]['pm25']
pm10 = data['HeWeather6'][0]['air_now_station'][0]['pm10']
main = data['HeWeather6'][0]['air_now_station'][0]['main'] # 主要污染物
air_sta = data['HeWeather6'][0]['air_now_station'][0]['air_sta'] # 观测站
pub_time = data['HeWeather6'][0]['air_now_station'][0]['pub_time']
return pm25, pm10
def main():
# -------常用数据-------------
url = weather_url(Regular_weather_data)
data = get_raw_data(url)
# --------空气质量------------
ac_url = 'https://free-api.heweather.com/s6/air/now?location=beijing&' + key # 把北京改为你的城市
ac_data = get_raw_data(ac_url)
# 实时天气及空气质量
tmp = LiveWeather(data)
pm25, pm10 = air_quality(ac_data)
return tmp, pm25, pm10
可以看到返回了很多数据,但是我只使用了tmp(温度),PM2.5,PM10三项数据,如果你有一定的python基础,可以自己修改。如果需要我帮忙修改的,可以在下面留言。
其他设置
主要的代码就是以上三个。
还需要安装依赖
sudo python3 -m pip install requests bs4 Adafruit_GPIO Pillow
以及一个第一步就手动安装的包。
在所有的依赖都安装完成后,可以测试一下代码了。
python3 display.py
成功了就是如下图的效果。
systemctl 服务
如果测试没有问题,我们就可以把它作为服务,以后就可以开启启动了
在 display.py 文件的目录下执行
pwd
获得当前目录,可以看到我的是 /home/pi/Nokia5110 复制下来
继续输入
sudo nano /etc/systemd/system/Nokia5110.service
添加如下代码
[Unit]
Description=Nokia 5110 Display
After=network.target
[Service]
# 下面两项改为你的用户名,一般都是pi
User=pi
Group=pi
Type=simple
# 将下面两个 /home/pi/Nokia5110 改为你的地址
WorkingDirectory=/home/pi/Nokia5110
ExecStart=/usr/bin/python3 /home/pi/Nokia5110/display.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
如果不放心配置是否正确,可以在shell中执行
/usr/bin/python3 /home/pi/Nokia5110/display.py
看看是否显示成功。
如果提示 -bash: /usr/bin/python3: 没有那个文件或目录 有可能是你没有安装python3或者python3的执行文件不在/usr/bin/python3。
如果是第二种情况(输入python3可以执行,/usr/bin/python3却不能执行的情况)可以输入
which python3
然后将 /usr/bin/python3 替换为 输出的结果即可。
输入 ctrl + o 保存 ctrl + x 退出。
依次执行:
sudo systemctl daemon-reload
sudo systemctl enable Nokia5110.service
sudo systemctl start Nokia5110
sudo systemctl status Nokia5110
如果是如上结果,并且屏幕正常显示,说明没有问题了。
到此,所有的设置结束。