本文目录
把闲置的树莓派变成一个桌面监视器
本文是树莓派板块下的第一篇文章,所以先介绍一下树莓派吧~
前言——树莓派是什么?
树莓派(Raspberry Pi) 是基于Linux的单板电脑 (Single Board Computer,简称SBC),由英国 树莓派基金会 开发,目的是以低价硬件及自由软件促进学校的基本计算机科学教育
当然,他也不仅仅只用来做教育,树莓派也可以成为广大爱好者玩家手中的diy利器,原因在于以下几点:
- 树莓派的生态十分丰富,有大量公司、团体和个人为其完善了从硬件到软件的生态链
- 树莓派体积小,使用方便,资料全,新人容易上手
- 基于庞大的生态链,你可以想象到的所有关于嵌入式的项目,基本上都有人用树莓派做过,这些项目也就成为了重要参考学习资料~
树莓派能做什么?
我们拿最新版的树莓派4B举例子。
-
硬件
树莓派的主CPU是博通公司的BCM系列SoC(System on Chip),最新版的树莓派4B使用的是 BCM2711,具有4核心1.5GHz A72(Quad-core Cortex-A72 (ARM v8) 64-bit SoC @ 1.5 GHz),这颗SoC具有丰富的外设比如GPIO、SPI、I2C、PWM、UART、PCIe总线、HDMI等,在树莓派4B上外设接口如下:
- 2 x Micro HDMI接口(4k@60fps)
- 1000M Ethernet接口
- 2xUSB3.0(通过PCIe总线连接USB控制器)、2xUSB2.0
- 40Pin 向下兼容 GPIO(实际上只有26Pin可用)
- 2-Lane MIPI CSI 相机接口
- 2-Lane MIPI DSI 显示器接口
- SD卡槽(用于加载操作系统和存储)
- PoE(Power Over Ethernet)供电接口
- 音频与复合视频接口(3.5MM音频口,可以输出音频或AV视频信号)
-
软件
树莓派能运行Raspbian(基于Debian的树莓派定制操作系统)、Ubuntu等嵌入式Linux操作系统,当然也有Windows IoT等奇怪的系统,甚至还有人在上面移植了RTOS…
当你已经可以运行Linux on ARM之后,你就可以把它当作一台标准计算机使用了~你可以在上面运行gcc、python解释器,配合使用各种库与软件包,能解锁这片SBC的全部潜力~
-
应用
具体用来做什么?这个问题我也研究了很久23333(因为我有至少3台树莓派在吃灰orz),我就说说我见过的~
- 运行网页服务器(LAMP/LNMP等)
- 当作联网监控使用(配合mjpg-streamer作视频推流)
- 当作直播工具使用(与上一条类似)
- 可以在上面跑OpenCV做些人脸识别、实时图像识别
- 运行Minecraft服务器(我试过很多次,树莓派4B的话,原版带几个玩家不成问题)
- 做成家庭云存储服务器(SMB、FTP等,可以提供媒体推流服务)
- 做成网络音乐播放器(树莓派版网抑云?)
- 当作无人机的主控(可以,但是功耗有点…(电池亲你还好吗?))
- 接上屏幕,当作开发工具(可以运行VsCode的设备都是开发工具#滑稽)
- 使用GPIO驱动多种外设、实现物联网控制等(这个玩法就太多了…)
国外的树莓派开源社区,有非常多的开源项目分享,是个找灵感的好地方
总之树莓派是个非常强大的开发平台,虽然也有同类产品的性能比他更优秀,但树莓派仍然是我认为的最好的入门嵌入式、Linux开发平台之一
。为什么?因为别的产品的生态远没有树莓派丰富,非专业人员拿到手根本不会用啊喂。。
EmoeMonitor
我为什么要做这个项目呢…说来话长。首先有如下场景:
- 我有一台闲置的树莓派4B,但我不知道用它来做什么好…
- Emoe的网站服务器经常卡死,我百思不得其解…同时又必须时不时登录后台来一次硬重启
- 我想做个能实时显示服务器的资源使用情况的监视器,使远端数据可视化,就像钟表一样。
- 额外的,我想玩玩IoT,搭建一个MQTT Broker并跑一套前后端来实现室内的电器控制与环境感测
基于以上需求,我就有了这个项目的原动力~
那么我需要做什么呢?怎么做?
原理图 & PCB
我想的是使用一块外接TFT作为数据可视化的窗口。但是网上卖的3.5寸左右TFT全都是与树莓派平行的,我要它能够直立起来,免得我伸脖子去看(
既然在淘宝上找不到类似的产品,那就只能自己造轮子咯——
我之前做 EmoePower 的时候买了3倍的物料,所以还剩下2块2.8寸 320×240分辨率的TFT屏幕,所以我直接用这块屏了~
老样子,使用KiCad绘制原理图与PCB,非常简单,用一个40pin排母夹住板子边缘就可以让PCB直立起来。
当然,我发挥了传统艺能,在板子后面贴了2个传感器。
- 原理图
- PCB
式姐好好看!
软件
服务器资源监控
首先是服务器的数据监控问题,最开始我使用了lnmp提供的php探针,具体说来就是从client端GET服务器上的php文件,那个文件中包含了服务器的实时运行状态和各种参数…但这种方法很快就被我弃用了。原因如下:
- 首先是有潜在安全隐患(好吧其实哪都有…)
- 其次会在accesslog里产生大量GET请求我看得好不爽啊555
这时候闲着无聊的宋学姐贴了过来,了解了我的需求之后,他用java写了一个server~
github链接如下
它会获取服务器的CPU与内存使用情况,还有服务器的运行时间(他偷了个懒,获取的是Java Server运行的时间…),将这些数据打包发给Client。
在客户端(Client),跟服务器的接口对接上后,获取到的数据将被保存到一个本地的json文件中以待使用。
点屏方案
说到这个我可就不困了啊)
点屏算是我的传统艺能之一了…但是这次我却犯了难。
在树莓派上想要驱动外置LCD-TFT,无非就是通过SPI(我用的SPI接口屏)推屏。那么有以下几种控制spi的方法,同时他们各有优劣,我做了些取舍
Method | Description | 弃用/启用原因 |
---|---|---|
BCM2835 Library | 纯C语言,如果用了它屏幕驱动要手写…(虽然我也写了) | 呜呜呜好麻烦 我不想用C写字符串解析与JSON解析什么的 |
Python | 使用adafruit提供的底层接口库blinka将标准python与MicroPython对接起来 | Python真香 嫖代码真香vv |
Linux Device Tree | 在Linux设备树上注册新SPI设备,并将Framebuffer映射过去 | 非常高效,而且这样就可以使用需要X11的GUI库了,但是我懒… |
人生苦短,我用一次Python好了。
那么重点介绍一下这套框架吧——
Blinka与MicroPython
标准的Python是没有底层硬件的API的,也就是说使用标准Python不能控制底层硬件的行为。
而MicroPython是专为嵌入式处理器量身剪裁的轻量级
Python解释器框架,通常是针对特定的处理器平台进行定制化,在MicroPython中就可以使用能直接控制硬件的API和函数。MicroPython可以在很多硬件平台上运行,比如STM32、AVR单片机(Arduino)、ESP32、K210等处理器上。
这是MicroPython的官方网站,还挺好玩的(主要是方便偷懒): https://micropython.org/
但是我们使用的是运行在树莓派上的标准Python,就像在普通的电脑上的Python一样,是不具有MicroPython的直通底层API的。这时候有许多第三方库就站出来了——比如 RPi.GPIO库,它就提供了所有直通树莓派底层硬件接口的Python函数。当然还有别的库,比如 GPIO Zero。
那么为什么还要Blinka呢…
简单来说,Blinka是 "套娃之娃"。它相当于一个翻译官,将Python代码翻译成底层硬件操作代码。(当然这个过程貌似是调用RPi.GPIO实现的)使用Blinka的好处是,它将所有与硬件操作相关的Python语法规范化了,这个规范当然就是CircuitPython
的规范咯(CircuitPython是一种MicroPython的变体)。Blinka是Adafruit开发出来用于在不同硬件平台上快速移植CircuitPython代码的。(Linux小企鹅比蟒蛇还是可爱点)
比如说,我有3块开发板,STM32F4、树莓派4B、ESP32,我如果想要点亮一个LED,需要写3套不同框架下的代码。但如果使用MicroPython框架,我只需要一份代码就可以了~
以下是MicroPython支持的处理器平台(图源Adafruit)
而我为什么不用别的Python库呢?因为别的Python库没有 ili9341(这块LCD屏幕的控制器) 的驱动库,而Adafruit家有一个库叫 adafruit_rgb_display
,这个库中包含了多达十几种屏幕控制器,其中就包含了ili9341。
这是Adafruit编写的Blinka的安装过程,请参考: https://learn.adafruit.com/circuitpython-on-raspberrypi-linux/installing-circuitpython-on-raspberry-pi
我还是简要概括1下Setup吧…
Install Blinka on Raspberry Pi
我运行的OS是 树莓派爱好者基地 编译的基于Debian的64位发行版,当然推荐使用官方Raspbian系统。
-
事前工作——当然是装好Raspbian系统辣
-
升级树莓派与Python3(注意,python2不支持)
在升级之前,记得换一下软件源,我推荐把apt的源和pip的源都换掉,可以清华源也可以阿里源。
sudo apt-get update
sudo apt-get upgrade
// 如果没装过pip,使用这条安装,装过就跳过这条
sudo apt-get install python3-pip
sudo pip3 install --upgrade setuptools
- 下载Adafruit提供的安装脚本进行安装
// 进入用户根目录
cd ~
sudo pip3 install --upgrade adafruit-python-shell
wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py
sudo python3 raspi-blinka.py
- 检查I2C与SPI是否启用
ls /dev/i2c* /dev/spi*
// 如果没问题,你应该看到这些内容:
/dev/i2c-1 /dev/spidev0.0 /dev/spidev0.1
如果需要更改使用的SPI port,请参考原文进行操作。
- 测试Blinka
新建一个 blinkatest.py
,然后抄代码:
import board
import digitalio
import busio
print("Hello blinka!")
# Try to great a Digital input
pin = digitalio.DigitalInOut(board.D4)
print("Digital IO ok!")
# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("I2C ok!")
# Try to create an SPI device
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
print("SPI ok!")
print("done!")
保存,然后run一下:
python3 blinkatest.py
看看输出信息如何~
Coding…
其实用了Python之后,就几乎没有什么代码量了:)
我不打算将所有代码开放,(毕竟我不希望我的服务器也被别人盯着…),放个删减版吧#手动和谐
import os
import time
import subprocess
import digitalio
import board
from PIL import Image, ImageDraw, ImageFont
import adafruit_rgb_display.ili9341 as ili9341
from adafruit_rgb_display.rgb import color565
import adafruit_bmp280
# 注册传感器,这里只用了BMP280,SHT30D没用
i2c = board.I2C()
bmp280 = adafruit_bmp280.Adafruit_BMP280_I2C(i2c)
bmp280.sea_level_pressure = 1013.25
# 屏幕接口配置
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D27)
reset_pin = digitalio.DigitalInOut(board.D22)
backlight = digitalio.DigitalInOut(board.D17)
backlight.switch_to_output()
backlight.value = True
BAUDRATE = 64000000 # SPI速率配置,使用PCB的话上到50MHz应该是没问题的,实测也不会花屏啥的
spi = board.SPI()
# Create the ST7789 display:
display = ili9341.ILI9341(
spi,
cs=cs_pin,
dc=dc_pin,
rst=reset_pin,
baudrate=BAUDRATE,
#width=320,
#height=240,
#x_offset=0,
#y_offset=0,
)
# 这里使用了PIL库新建了个320x240的图像对象
# Create blank image for drawing.
width = 320
height = 240
img = Image.new("RGB", (width, height))
rotation = 270
# Get drawing object to draw on image.
draw = ImageDraw.Draw(img)
# Draw a black filled box to clear the image.
draw.rectangle((0, 0, width, height), outline=0, fill=(0, 0, 0))
display.image(img, rotation)
padding = -2
top = padding
bottom = height - padding
x = 0
while True:
draw.rectangle((0, 0, width, height), outline=0, fill=0)
# 获取树莓派的信息
cmd = "hostname -I | cut -d' ' -f1"
IP = "IP: " + subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "top -bn1 | grep load | awk '{printf \"CPU Load: %.2f\", $(NF-2)}'"
CPU = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%s MB %.2f%%\", $3,$2,$3*100/$2 }'"
MemUsage = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = 'df -h | awk \'$NF=="/"{printf "Disk: %d/%d GB %s", $3,$2,$5}\''
Disk = subprocess.check_output(cmd, shell=True).decode("utf-8")
cmd = "cat /sys/class/thermal/thermal_zone0/temp | awk '{printf \"CPU Temp: %.1f C\", $(NF-0) / 1000}'" # pylint: disable=line-too-long
Temp = subprocess.check_output(cmd, shell=True).decode("utf-8")
# 获取传感器(BMP280)的信息
tmp = "Temp(board): %0.1f C" % bmp280.temperature
pres = "Pressure: %0.1f hPa" % bmp280.pressure
alti = "Altitude = %0.2f meters" % bmp280.altitude
# 获取服务器信息...诶,被FLoydfish吃了![]~( ̄▽ ̄)~*
# Set font to draw
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
y = top
draw.text((x, y), "RPI local " + IP, font=font, fill="#FFFFFF")
y += font.getsize(IP)[1]
draw.text((x, y), "RPI's " + CPU, font=font, fill="#FFFF00")
y += font.getsize(CPU)[1]
draw.text((x, y), "RPI's " + MemUsage, font=font, fill="#00FF00")
y += font.getsize(MemUsage)[1]
draw.text((x, y), "RPI's " + Disk, font=font, fill="#0000FF")
y += font.getsize(Disk)[1]
draw.text((x, y), "RPI's " + Temp, font=font, fill="#FF00FF")
# Set font to draw
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 16)
y += 18
draw.text((x, y), tmp, font=font, fill="#FF0000")
y += 16
draw.text((x, y), pres, font=font, fill="#FF0000")
y += 16
draw.text((x, y), alti, font=font, fill="#FF0000")
#y += 16
# 显示
display.image(img, rotation)
# 1秒1刷新
time.sleep(1)
运行效果
看我使用柔焦滤镜拍出来的——
(焦锐奶化,毒德大学(bushi))
Fin
如果你对这个项目感兴趣,或者想要看看我的PCB设计文件,请联系我(因为不是什么大项目所以懒得开个repo了…)
树莓派还有很多的玩法,我们以后还会继续探索的~
下一期——MQTT
与IoT
!