在我之前的文章中,我解释了如何将 Raspberry Pi Zero 改造成一个极简、便携、随时随地可用的计算机系统,虽然体积小巧,但实际上可以完成有用的工作。从那时起,我进行了一些迭代,这些迭代证明很有趣,并使这个小 Pi 更加有用。请继续阅读以了解我所做的工作。
公路旅行之后
我最初的 Pi Zero 设置在前往惠特比的公路旅行中证明了它的价值,但之后,它在很大程度上被搁置在“待定”架子上,等待着另一项任务。每周都会开机应用更新,除此之外,它一直处于闲置状态。然后有一天,当我浏览来自各种 Pi 供应商的电子邮件时,我偶然发现了一个(略微)降价的电子墨水屏显示器优惠:嗯……还有一个用于 Pi Zero 的版本。我可以用它做什么呢?
ModMyPi 正在销售一款相当简洁的显示屏和驱动板组合,以及一个顶部带有透明窗口的小巧的保护壳。我阅读了通常的评论,除了一个关于板子非常紧的评论外,听起来都是积极的。我订购了它,几天后就送到了。我从产品描述中注意到显示板没有安装 GPIO 接头,所以我订购了一个 Pi Zero WH(无线 + 预装接头),以省去自己焊接的麻烦。
需要一些组装
与大多数此类产品一样,需要进行一些自行组装,所以我小心地打开了盒子,并将零件摆放在桌子上。除了表带的荒谬插槽(?!)和侧面的一些奇怪的孔,以便小手指可以按下显示屏上的五个 I/O 按钮外,这个保护壳制作精良。“我可以要一个没有孔的顶部吗?”我在评论页面上询问。“不行。”好吧。
拆开保护壳后,就该打开显示屏的盒子了。首先是一个设计精美的板子,Pi-Supply 网站上有清晰的说明。显示屏非常薄(0.95 毫米),以至于我差点把它和气泡膜一起扔掉。
第一步是将显示板安装到 Pi Zero 上。我检查以确保在将显示电缆连接到驱动板时可以将其连接到 Pi,并决定,以我的香肠手指,我先连接显示屏,让它在微风中摆动,然后再将驱动板连接到 Pi。我小心地将板子在 GPIO 引脚上对齐,并将它们固定到位后,我将显示屏“屏幕”折叠起来放在板子的顶部。将背靠背的板子安装到位后,我非常非常小心地将组件塞入保护壳中。非常紧吗?是的,你没开玩笑,但我把它安全地安装到位并扣上了顶部,看起来没有任何东西被损坏。呼!
如何设置你的显示屏
我将跳过一大段关于折腾的内容,并建议你参考制造商的说明。总而言之,经过几次安装、重启和咖啡后,我设法让电子墨水屏显示器工作了!现在我所要做的就是弄清楚如何使用它。
使用像我的“TravelPi”这样的小型设备的主要挑战之一是你无法像在大型机器上那样访问到那么多的屏幕空间。不过,我喜欢这个设备的尺寸和功率,因此这真的是一个关于你从中获得什么东西的折衷方案。例如,有一个可以通过 HDMI 端口访问的单个屏幕,我使用 tmux 将其分成四个独立的、可用的窗格。如果我真的需要紧急查看其他内容,我可以随时 Ctrl+Z 进入另一个提示符并进行必要的配置,但这很麻烦。
我想查看各种设置,也许还可以查看一些系统设置,而电子墨水屏显示器使我能够做到这一切!正如你可以从下面的图片中看到的那样,我最终得到了一个非常实用的信息面板,它由一个简单的(相对简单的)Python 脚本(qv)手动或通过 crontab 条目每 10 分钟更新一次。制造商声明,如果你希望显示器长时间使用,更新频率应“不超过 1Hz”。十分钟就足够了,谢谢。
以下是我希望能够一目了然地看到的内容
主机名 | 以及设备序列号 |
IP 地址 | 当前内部 IP 地址 |
VPN 状态 | 未激活/国家/IP 地址 |
Tor 状态 | 未激活/IP 地址 |
“使用率” | 已使用的磁盘空间和内存百分比 |
运行时间 | 看到这些长时间的运行时间真是令人满意 |
这就是它:一个与 Pi Zero 尺寸相同且深度为 1 英寸的显示屏。

如何填充显示屏
现在我需要填充显示屏。似乎现在很常见的情况是,电子墨水屏支持软件是用 Python 编写的,当然,Python 是大多数 Linux 发行版的标准配置。免责声明:Python 不是我的第一(开发)语言,但下面的代码对我来说有效。它可能对你也有效。
#!/usr/bin/env python
import os
import sys
import time
import datetime
import socket
import netifaces as ni
import psutil
import subprocess
from netifaces import AF_INET, AF_INET6, AF_LINK, AF_PACKET
from papirus import PapirusText, PapirusTextPos, Papirus
from subprocess import check_output
from datetime import timedelta
rot = 0
screen = Papirus(rotation = rot)
fbold = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf'
fnorm = '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf'
text = PapirusTextPos(rotation = rot)
def GetBootTime():
return datetime.datetime.fromtimestamp(psutil.boot_time())
def GetUptime():
with open('/proc/uptime','r') as f:
uptime_seconds = float(f.readline().split()[0])
u = str(timedelta(seconds = uptime_seconds))
duration,junk = u.split(".")
hr,mi,sc = duration.split(":")
return "%sh %sm %ss" % ( hr,mi,sc )
def getHostname():
hostname = socket.gethostname()
return hostname
def getWiFiIPaddress():
try:
ni.interfaces()
[ 'wlan0', ]
return ni.ifaddresses('wlan0')[AF_INET][0]['addr']
except:
return 'inactive'
def getVPNIPaddress():
try:
ni.interfaces()
[ 'tun0', ]
return ni.ifaddresses('tun0')[AF_INET][0]['addr']
except:
return 'inactive'
def GetTmuxEnv():
if 'TMUX_PANE' in os.environ:
return ' (t)'
return ' '
def GetCPUserial():
cpuinfo = subprocess.check_output(["/bin/cat", "/proc/cpuinfo"])
cpuinfo = cpuinfo.replace("\t","")
cpuinfo = cpuinfo.split("\n")
[ legend, cpuserial ] = cpuinfo[12].split(' ')
cpuserial = cpuserial.lstrip("0")
return cpuserial
def GetMemUsed():
memUsed = psutil.virtual_memory()[2]
return memUsed
def GetDiskUsed():
diskUsed = psutil.disk_usage('/')[3]
return diskUsed
def CheckTor():
try:
TS = "active: pid %s" %check_output(['pidof','tor'])
except:
TS = 'inactive'
return TS
def CheckVPN():
return VPNlo
# ---------------------------------------------------------------------------
def main():
pass
if __name__ == '__main__':
main()
VPNlo = 'inactive'
if (len(sys.argv) == 2):
try:
VPNlo = sys.argv[1]
except:
VPNlo = 'inactive'
text = PapirusTextPos(False,rotation=rot)
text.AddText("%s %s %s"% (getHostname(),GetCPUserial(),GetTmuxEnv()),x=1,y=0,size=12,invert=True,fontPath=fbold)
text.AddText("IP %s" % getWiFiIPaddress(),x=1,y=16,size=12,fontPath=fnorm)
if ( getVPNIPaddress() == 'inactive' ):
text.AddText("VPN %s" % CheckVPN(),x=1,y=30,size=12,fontPath=fnorm)
else:
text.AddText("VPN %s" % getVPNIPaddress(),x=1,y=30,size=12,fontPath=fnorm)
text.AddText("TOR %s" % CheckTor(),x=1,y=44,size=12,fontPath=fnorm)
text.AddText("MEM %s%% DISK %s%% used" % (GetMemUsed(),GetDiskUsed()),x=1,y=58,size=12,fontPath=fnorm,maxLines=1)
text.AddText("UPTIME %s" % GetUptime(),x=1,y=72,size=12,fontPath=fnorm)
text.WriteAll()
sys.exit(0)
通常,该脚本在没有任何参数的情况下运行,并由我编写的一系列 Bash 脚本调用以启动各种子系统;这些子系统又从用 Whiptail 编写的菜单系统中调用,Whiptail 非常通用。在 VPN 系统的情况下,我有一个接入点列表可供选择,这些接入点会更新显示屏上的位置。最初,我使用位置名称(例如,檀香山)调用显示更新程序,但在那时,我无法显示 VPN IP 地址,因为我不知道它。
dispupdate.py ${accesspoint}
openvpn --config $PATH/Privacy-${accesspoint}.conf --auth-user-pass credfile
当显示更新程序再次运行时(在 VPN 启动脚本之外),IP 地址可以从 tun0 接口读取,并且显示屏会使用 IP 地址更新。我可能会稍后更改它,但现在它工作正常。我使用 PapirusTextPos 函数(而不是 PapirusText),因为这允许在更新显示屏之前写入多行,从而实现更快的写入速度。text.WriteAll() 函数执行实际更新。
添加更多软件
我对最初选择的应用程序非常满意,但由于我已经设法将整个安装缩小到 1.7GB,我还有大量的可用空间。所以,我决定看看是否还有其他有用的东西。以下是我添加的内容
Irssi | IRC 客户端 |
FreeBSD 游戏 | 仍然有很多文本模式游戏可以享受 |
nmon | 一个非常全面的类似 top 的实用程序,用于系统的各个方面 |
Newsbeuter | 文本模式 Atom/RSS feed 阅读器 |
我仍然有大约 300MB 的可用空间,可以达到 2GB,所以我可能会添加更多。
我们需要谈谈 Kevin 蓝牙
细心的读者会记得我对蓝牙的厌恶,以及尝试将基于终端的软件与蓝牙设备配对。当我购买新的 Pi 时,我意识到我不得不再次将该死的东西与键盘配对。哦,天哪!但是一次搜索引擎会话和一杯令人平静的咖啡使我实际上能够做到这一点!过程大致如下
sudo su
bluetoothctl {enter}
[bluetooth]#
[bluetooth]# scan on
Discovery started
[CHG] Controller B8:27:EB:XX:XX:XX Discovering: yes
[bluetooth]# agent on
Agent registered
[NEW] Device B2:2B:XX:XX:XX:XX Bluetooth Keyboard
Attempting to pair with B2:2B:XX:XX:XX:XX
[CHG] Device B2:2B:XX:XX:XX:XX Connected: yes
[agent] PIN code: 834652
[CHG] Device B2:2B:XX:XX:XX:XX Modalias: usb:v05ACp0220d0001
[CHG] Device B2:2B:XX:XX:XX:XX UUIDs: zzzzz
[CHG] Device B2:2B:XX:XX:XX:XX UUIDs: yyyyy
[CHG] Device B2:2B:XX:XX:XX:XX ServicesResolved: yes
[CHG] Device B2:2B:XX:XX:XX:XX Paired: yes
Pairing successful
[CHG] Device B2:2B:XX:XX:XX:XX ServicesResolved: no
[CHG] Device B2:2B:XX:XX:XX:XX Connected: no
[bluetooth]# trust B2:2B:XX:XX:XX:XX
[CHG] Device B2:2B:XX:XX:XX:XX Trusted: yes
Changing B2:2B:XX:XX:XX:XX trust succeeded
[CHG] Device B2:2B:XX:XX:XX:XX RSSI: -53
[bluetooth]# scan off
[CHG] Device B2:2B:XX:XX:XX:XX RSSI is nil
Discovery stopped
[CHG] Controller B8:27:EB:XX:XX:XX Discovering: no
[bluetooth]# exit
Agent unregistered
$
我惊呆了!不,真的。我配对了我的另一个键盘,现在正在考虑配对一个扬声器,但我们拭目以待。那天晚上我喝了一杯啤酒来庆祝我新获得的“l33t”技术技能!这是一个关于如何做到这一点的优秀指南。
又一个硬件改装
直到最近,我一直在使用我能负担得起的尽可能大的高质量 microSDHC 卡,并且为了以防万一,我使用基于 rsync 的 rpi-clone 创建了一个备份副本。然而,在阅读了网上各种文章,人们抱怨由于电源问题、不干净的关机和其他事故导致卡损坏后,我决定投资购买一张更高质量的卡,希望它能够经受住所有这一切以及更多。如果你要长途旅行并且真的需要在目的地让你的软件工作,这一点很重要。
经过长时间的搜索,我找到了ATP 工业级 MicroSD/MicroSDHC 卡,这些卡被评为军用规格,适用于要求苛刻的应用。这听起来很完美。然而,质量是有代价的,并且(在这种情况下)容量有限。为了让我的钱包高兴,我将自己限制为 8GB 卡,这对于一台工作的计算机来说可能听起来不多,但考虑到我真正有 8GB 中的 5.3GB 可用空间,它工作得很好。我还获得了一定程度的保证,这是更大但质量较低的卡无法给我的,而且我可以创建该卡的 ISO,如果需要,该 ISO 足够小,可以通过电子邮件发送。结果!
下一步是什么?
Zero 变得越来越强大,只需要更多地外出使用。从技术上讲,我现在已经走到了我所能达到的最远的地方,任何其他变化都将是微小而渐进的。
本文最初发布在 Peter Garner 的博客上,根据 CC BY-NC-ND 4.0 许可获得许可,并经作者许可在此处重复使用。
评论已关闭。