使用 Raspberry Pi 创建倒计时时钟

使用 Raspberry Pi 和电子墨水屏显示器开始倒计时您的下一个假期。
76 位读者喜欢这篇文章。
Clocks

Matteo Ianeselli。由 Opensource.com 修改。CC-BY-3.0。

2021 年,圆周率日已经过去,留下了美好的回忆和大量的 Raspberry Pi 项目可以尝试。任何节日后的日子都可能很难熬,尤其是在精神振奋、玩得尽兴之后回到工作岗位,圆周率日也不例外。当我们展望三月十五时,我们可能会渴望前一天的快乐。但亲爱的圆周率日庆祝者们,不要害怕!因为今天,我们开始了到下一个圆周率日的漫长倒计时!

好吧,说真的。我制作了一个圆周率日倒计时器,您也可以做到!

前段时间,我购买了一个Raspberry Pi Zero W,最近用它来找出我的 WiFi 为什么这么差。我对为这款小巧的 Zero W 配备电子墨水屏显示器也很感兴趣。我没有很好的用途,但是,天哪,它看起来很有趣!我购买了一个小巧的 2.13 英寸Waveshare 显示器,它完美地安装在 Raspberry Pi Zero W 的顶部。它很容易安装:只需将显示器滑到 Raspberry Pi 的 GIPO 引脚上即可。

我为这个项目使用了Raspberry Pi OS,虽然肯定可以用其他操作系统完成,但下面使用的raspi-config命令在 Raspberry Pi OS 上最容易获得。

设置 Raspberry Pi 和电子墨水屏显示器

设置 Raspberry Pi 以与电子墨水屏显示器配合使用需要您在 Raspberry Pi 软件中启用串行外设接口 (SPI),安装 BCM2835 C 库(以访问 Broadcom BCM 2835 芯片上的 GPIO 函数),并安装 Python GPIO 库来控制电子墨水屏显示器。最后,您需要安装 Waveshare 库,以便使用 Python 处理 2.13 英寸显示器。

以下是如何执行这些任务的分步指南。

启用 SPI

启用 SPI 最简单的方法是使用 Raspberry Pi raspi-config 命令。 SPI 总线允许串行数据通信与设备一起使用——在本例中,是电子墨水屏显示器。

$ sudo raspi-config

从弹出的菜单中,选择 Interfacing Options -> SPI -> Yes 以启用 SPI 接口,然后重新启动。

安装 BCM2835 库

如上所述,BCM2835 库是 Raspberry Pi 上的 Broadcom BCM 2385 芯片的软件,它允许访问 GPIO 引脚并能够使用它们来控制设备。

在我撰写本文时,用于 Raspberry Pi 的最新版本的 Broadcom BCM 2835 库是 v1.68。要安装这些库,您需要下载软件 tarball 并使用 make 构建和安装该软件。

# Download the BCM2853 libraries and extract them
$ curl -sSL http://www.airspayce.com/mikem/bcm2835/bcm2835-1.68.tar.gz -o - | tar -xzf -

# Change directories into the extracted code
$ pushd bcm2835-1.68/

# Configure, build, check and install the BCM2853 libraries
$ sudo ./configure
$ sudo make check
$ sudo make install

# Return to the original directory
$ popd

安装所需的 Python 库

您还需要一些 Python 库才能使用 Python 控制电子墨水屏显示器,即 RPi.GPIO pip 包。您还需要用于绘制形状的 python3-pil 包。显然,PIL 包几乎已经死了,但是有一个替代方案,Pillow。我没有为这个项目测试过 Pillow,但它可能会起作用。

# Install the required Python libraries
$ sudo apt-get update
$ sudo apt-get install python3-pip python3-pil
$ sudo pip3 install RPi.GPIO

注意:这些说明适用于 Python 3。您可以在 Waveshare 的网站上找到 Python 2 说明。

下载 Waveshare 示例和 Python 库

Waveshare 维护着一个 Git 存储库,其中包含用于处理其电子墨水屏显示器的 Python 和 C 库,以及一些演示如何使用它们的示例。对于这个倒计时时钟项目,您将克隆这个存储库并使用 2.13 英寸显示器的库。

# Clone the WaveShare e-Paper git repository
$ git clone https://github.com/waveshare/e-Paper.git

如果您使用的是不同的显示器或来自另一家公司的产品,则需要使用适合您显示器的软件。

Waveshare 在其网站上提供了上述大部分内容的说明。

获取有趣的字体(可选)

您可以以您喜欢的任何方式显示您的计时器,但为什么不使用一些风格呢?找到一个很酷的字体来使用!

那里有大量的开放字体许可证字体可用。我特别喜欢 Bangers。如果您看过 YouTube,您就会看到它——它到处都在使用。可以将其下载并放入您用户的本地共享字体目录中,以便任何应用程序都可以使用,包括这个项目。

# The "Bangers" font is a Open Fonts License licensed font by Vernon Adams (https://github.com/vernnobile) from Google Fonts
$ mkdir -p ~/.local/share/fonts
$ curl -sSL https://github.com/google/fonts/raw/master/ofl/bangers/Bangers-Regular.ttf -o fonts/Bangers-Regular.ttf

创建一个圆周率日倒计时器

现在您已经安装了与电子墨水屏显示器配合使用的软件和一个有趣的字体,您可以构建一些很酷的东西:一个倒计时到下一个圆周率日的计时器!

如果您愿意,您可以直接从这个项目的GitHub 仓库中获取 countdown.py Python 文件,然后跳到本文结尾。

对于好奇的人,我将逐节分解该文件。

导入一些库

#!/usr/bin/python3
# -*- coding:utf-8 -*-
import logging
import os
import sys
import time

from datetime import datetime
from pathlib import Path
from PIL import Image,ImageDraw,ImageFont

logging.basicConfig(level=logging.INFO)

basedir = Path(__file__).parent
waveshare_base = basedir.joinpath('e-Paper', 'RaspberryPi_JetsonNano', 'python')
libdir = waveshare_base.joinpath('lib')

在开始时,Python 脚本导入了脚本后面使用的一些标准库。您还需要从 PIL 包中添加 ImageImageDrawImageFont,您将使用它们绘制一些简单的几何形状。最后,为包含用于处理 2.13 英寸显示器的 Waveshare Python 库的本地 lib 目录设置一些变量,您可以稍后使用它们从本地目录加载库。

字体大小辅助函数

脚本的下一部分有一个辅助函数,用于设置您选择的字体 Bangers-Regular.ttf 的字体大小。它接受一个整数作为字体大小,并返回一个可以与显示器一起使用的 ImageFont 对象。

def set_font_size(font_size):
    logging.info("Loading font...")
    return ImageFont.truetype(f"{basedir.joinpath('Bangers-Regular.ttf').resolve()}", font_size)

倒计时逻辑

接下来是一个小函数,用于计算这个项目的核心内容:距离下一个圆周率日还有多长时间。如果是在 1 月份,计算还剩下多少天会相对简单,但您还需要考虑圆周率日是否已经过去(悲伤),如果是,则计算距离您再次庆祝还有多少天。

def countdown(now):
    piday = datetime(now.year, 3, 14)

    # Add a year if we're past PiDay
    if piday < now:
        piday = datetime((now.year + 1), 3, 14)

    days = (piday - now).days

    logging.info(f"Days till piday: {days}")
    return day

主函数

最后,您将进入主函数,该函数初始化显示器并开始向其写入数据。在这种情况下,您将写入一条欢迎消息,然后开始倒计时到下一个圆周率日。但首先,您需要加载 Waveshare 库。

def main():

    if os.path.exists(libdir):
        sys.path.append(f"{libdir}")
        from waveshare_epd import epd2in13_V2
    else:
        logging.fatal(f"not found: {libdir}")
        sys.exit(1)

上面的代码片段检查以确保库已下载到倒计时脚本旁边的目录中,然后加载 epd2in13_V2 库。如果您使用的是不同的显示器,则需要使用不同的库。如果您愿意,您也可以编写自己的库。我发现阅读 Waveshare 随显示器提供的 Python 代码很有趣。它比我想象的要简单得多,虽然有点乏味。

下一段代码创建一个 EPD(电子墨水屏显示器)对象来与显示器交互并初始化硬件。

    logging.info("Starting...")
    try:
        # Create an a display object
        epd = epd2in13_V2.EPD()

        # Initialize the displace, and make sure it's clear
        # ePaper keeps it's state unless updated!
        logging.info("Initialize and clear...")
        epd.init(epd.FULL_UPDATE)
        epd.Clear(0xFF)

关于电子墨水屏的一个有趣的题外话:它仅在将像素从白色更改为黑色或反之亦然时才使用电量。这意味着当从设备上移除电源或应用程序因任何原因停止时,屏幕上的任何内容都会保留。从功耗的角度来看,这很棒,但这也意味着您需要在启动时清除显示器,否则您的脚本只会覆盖屏幕上已有的内容。因此,epd.Clear(0xFF) 用于在脚本启动时清除显示器。

接下来,创建一个“画布”,您将在其中绘制其余的显示输出。

    # Create an image object
    # NOTE: The "epd.heigh" is the LONG side of the screen
    # NOTE: The "epd.width" is the SHORT side of the screen
    # Counter-intuitive...
    logging.info(f"Creating canvas - height: {epd.height}, width: {epd.width}")
    image = Image.new('1', (epd.height, epd.width), 255)  # 255: clear the frame
    draw = ImageDraw.Draw(image)

这与显示器的宽度和高度相匹配——但它有点违反直觉,因为显示器的短边是宽度。我认为长边是宽度,所以这只是需要注意的一点。请注意,epd.heightepd.width 由 Waveshare 库设置,以对应于您正在使用的设备。

欢迎消息

接下来,您将开始绘制一些东西。这涉及在您上面创建的“画布”对象上设置数据。这不会将其绘制到电子墨水屏显示器上——您现在只是在构建您想要的图像。创建一个庆祝圆周率日的小欢迎消息,其中包含一块馅饼的图像,这是我专门为这个项目绘制的。

可爱吧?

    logging.info("Set text text...")
    bangers64 = set_font_size(64)
    draw.text((0, 30), 'PI DAY!', font = bangers64, fill = 0)

    logging.info("Set BMP...")
    bmp = Image.open(basedir.joinpath("img", "pie.bmp"))
    image.paste(bmp, (150,2))

最后,终于,您可以显示您绘制的画布了,但这有点虎头蛇尾。

    logging.info("Display text and BMP")
    epd.display(epd.getbuffer(image))

上面的代码位更新显示器以显示您绘制的图像。

接下来,准备另一张图像来显示您的倒计时器。

圆周率日倒计时器

首先,创建一个新的图像对象,您可以使用它来绘制显示器。此外,设置一些新的字体大小以用于图像。

    logging.info("Pi Date countdown; press CTRL-C to exit")
    piday_image = Image.new('1', (epd.height, epd.width), 255)
    piday_draw = ImageDraw.Draw(piday_image)

    # Set some more fonts
    bangers36 = set_font_size(36)
    bangers64 = set_font_size(64)

要显示像倒计时这样的跳动器,更有效的方法是更新图像的一部分,仅更改显示器中您想要绘制的数据中已更改的部分。下一段代码准备显示器以这种方式运行。

    # Prep for updating display
    epd.displayPartBaseImage(epd.getbuffer(piday_image))
    epd.init(epd.PART_UPDATE)

最后,你将进入计时器部分,启动一个无限循环,检查距离下一个 Pi Day 还有多久,并在电子纸显示屏上显示倒计时。如果它实际上 Pi Day,你可以用一个简短的庆祝信息来处理。

    while (True):
        days = countdown(datetime.now())
        unit = get_days_unit(days)

        # Clear the bottom half of the screen by drawing a rectangle filld with white
        piday_draw.rectangle((0, 50, 250, 122), fill = 255)

        # Draw the Header
        piday_draw.text((10,10), "Days till Pi-day:", font = bangers36, fill = 0)

        if days == 0:
            # Draw the Pi Day celebration text!
            piday_draw.text((0, 50), f"It's Pi Day!", font = bangers64, fill = 0)
        else:
            # Draw how many days until Pi Day
            piday_draw.text((70, 50), f"{str(days)} {unit}", font = bangers64, fill = 0)

        # Render the screen
        epd.displayPartial(epd.getbuffer(piday_image))
        time.sleep(5)

脚本的最后一部分处理一些错误,包括一些捕捉键盘中断的代码,以便你可以使用 Ctrl+C 停止无限循环,以及一个小函数来打印 "day" 或 "days",具体取决于输出是否应该是单数形式(对于一年中那唯一的一天,它才是合适的)。

    except IOError as e:
        logging.info(e)

    except KeyboardInterrupt:
        logging.info("Exiting...")
        epd.init(epd.FULL_UPDATE)
        epd.Clear(0xFF)
        time.sleep(1)
        epd2in13_V2.epdconfig.module_exit()
        exit()


def get_days_unit(count):
    if count == 1:
        return "day"

    return "days"


if __name__ == "__main__":
    main()

就这样,你就有了一个倒计时并显示距离 Pi Day 还有多少天的脚本!这是一张在我的 Raspberry Pi 上的动作照片(加速了 86,400 倍;我没有足够的磁盘空间来保存一整天的视频)。

安装 systemd 服务(可选)

如果你希望倒计时显示在系统启动时运行,而无需登录并运行脚本,你可以将可选的 systemd 单元作为 systemd 用户服务 安装。

将 GitHub 上的 piday.service 文件复制到 ${HOME}/.config/systemd/user,首先创建该目录(如果它不存在)。然后你可以启用并启动该服务。

$ mkdir -p ~/.config/systemd/user
$ cp piday.service ~/.config/systemd/user
$ systemctl --user enable piday.service
$ systemctl --user start piday.service

# Enable lingering, to create a user session at boot
# and allow services to run after logout
$ loginctl enable-linger $USER

该脚本将输出到 systemd 日志,可以使用 journalctl 命令查看输出。

Pi Day 看起来越来越近了!

现在 你得到了它!一个 Pi Day 倒计时计时器,使用 Raspberry Pi Zero W 显示在电子纸显示屏上,并通过 systemd 单元文件在系统启动时启动!现在距离我们再次聚在一起庆祝 Raspberry Pi 这个梦幻般的设备只有 350 多天了。而且通过我们的小项目,我们可以一目了然地看到还有多少天。

但事实上,任何人都可以全年将 Pi Day 放在心中,所以尽情地用你自己的 Raspberry Pi 创造一些有趣和具有教育意义的项目吧!

接下来阅读什么
标签
Chris Collins
Chris Collins 是红帽的 SRE,也是 OpenSource.com 的通讯员,他对自动化、容器编排及其周围的生态系统充满热情,并且喜欢在家中重现企业级技术以获得乐趣。

1 条评论

看起来很棒

Creative Commons License本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
© . All rights reserved.