树莓派使用Nokia5110屏幕 高级版

我有一块(未)吃灰多年的树莓派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可以执行,都可以试试

如果执行成功会出现

这个图形,这里我偷懒用的网上的图片。

这时候就可以开始下一步了。

添加代码

我的屏幕主要显示如下几项信息:

  1. 时间

  2. 天气(使用的和风天气的api)

  3. CPU信息

  4. ram信息

  5. 磁盘信息

理论上可以定制无限多的信息,只要你觉得自己看得过来。

主代码(显示代码)

# 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

如果是如上结果,并且屏幕正常显示,说明没有问题了。

到此,所有的设置结束。

代码下载

点击下载