bbServer 制作过程与经验分享
【简介】
bbServer 是一台高性能、低功耗、酷炫的家用服务器,通过单板电脑特有的 GPIO 完成屏幕和灯带的控制,配合单面镜和半透镜形成深渊灯的效果。作者的个人应用场景是用来运行 Minecraft 服务器,让游戏内的活动与服务器外围设备产生联动,例如当有玩家进入服务器时,会在 OLED 屏幕上显示,并且伴随着光效。
开源链接:
https://github.com/RealCorebb/bbServer
视频链接:
https://www.bilibili.com/video/BV1j24y1a7Ut/
【故事起因】
我一直想 DIY 一台能够放在家里 24 小时开机的服务器。我希望这台服务器功耗足够低,这样就不会被妈妈打。因为我想用它来运行 Minecraft 服务器,所以性能也需要稍微好一点。然而,低功耗和高性能之间存在着矛盾,我只有选择能耗比更高的芯片。
所以,我选择了香橙派5这类 ARM 架构的单板电脑,因为它省电、体积小,性能也高,而且可以像单片机一样控制一些电子元件。因此,我在服务器上添加 OLED 屏幕和 WS2812 RGB 灯带,借助 CircuitPython 就可以根据自己的想法尽情发挥,制作出这样一台低功耗、迷你、又好玩又酷炫的服务器。
【bbServer制作步骤】
1.购买所需物料
在开始制作bbServer之前,您需要准备以下物料:
(1)香橙派5,这是一款性价比非常高的RK3588S单板电脑,RK3588S是一个8nm制程ARM架构的SOC,有8核心(6大核+2小核),用来开中小型Minecraft服务器是非常稳妥且省电的。
(2)2.42寸OLED屏幕 (4PIN I2C版本)
(3)WS2812-2020 寻址RGB灯带(90LEDS/M)
(4)3D打印 外壳
(5)单面镜+30%半透镜
(6)M2硬盘(2230或2242尺寸)
(7)散热片(19*19*5mm)
(8)3007涡轮散热风扇
(9)5V4A电源适配器
2.组装硬件
将M.2硬盘插入香橙派5上的M.2插槽中。
将散热片安装在香橙派5的芯片上,然后将其插入3D打印的外壳中,并将四周的固定螺丝扭紧。香橙派5本身侧边有一个开关机的按键,如果需要使用的话也可以3D打印一个较大的按钮模型,卡在外壳与开关按键之间。
安装3007涡轮散热风扇,可以装在芯片旁边,出气口朝外,将正负极分别接到5V和GND。如果想要更安静一点,也可以接到3.3V。
将WS2812-2020 RGB灯带与OLED屏幕连接到香橙派5的GPIO引脚上,确保连接正确。因为设计的外壳想尽量保持小巧、紧凑,所以用一般的2.54间距的杜邦线会太高,装不进去。解决办法就是将杜邦线的胶壳去掉(用镊子撬开卡口,把线拿出来即可),可以用热缩管或胶布套住端子部分,避免相互之间接触导电。
具体的IO引脚和接线图如下:
安装好之后可以跳到第3大步:安装系统、环境,测试一下LED和屏幕是否正常点亮,然后接着完成最后的组装。
在验证硬件正常后,将单面镜和30%半透镜组装到3D打印的外壳中,注意尽量戴上干净的手套,避免手指印。
OLED屏幕可以一边用胶布固定在RJ45网口上,另一边用杜邦线顶着。摆放在正中间靠后的位置,然后将单面镜卡进OLED屏幕与LED之间。
接着,再盖上半透镜就大功告成啦。
3.安装系统
香橙派5官方提供了Ubuntu、Debian、Android系统,也有在单板电脑上比较出名的Armbian系统。但目前的情况Armbian有一些BUG,导致I2C和SPI功能不正常,无法使用LED和OLED。所以这里选择的是官方的Ubuntu系统。
完整的文档可以参考官方的使用说明书:
http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-pi-5.html
大致步骤如下:
(1)准备一张TF卡,使用balenaEtcher软件将官网下载的Ubuntu镜像写入TF卡中
(2)插入TF卡、M2硬盘,开机。连接网线,使用Xshell连接香橙派或使用HDMI连接显示器操作。默认用户名为:root,密码为:orangepi
(3)在终端控制台输入指令nand-sata-install,选择Install the bootloader on SPI Flash,等待几分钟直至出现Done
(4)将镜像写入M2硬盘中,指令如下:
dd bs=1M if=<镜像路径> of=/dev/nvme0n1 status=progress
例如:
dd bs=1M if=Orangepi5_x.x.x_ubuntu.img of=/dev/nvme0n1 status=progress
(5)写入完成后,就可以输入poweroff指令关机,拔掉TF卡,再开机,以后就都是用M2硬盘作为系统盘开机啦。M2硬盘比起TF卡更耐用,更适合长时间运行,速度也更快。
4.配置环境
在完成以上步骤后,这就是一台arm架构的Ubuntu系统的电脑了,你可以使用绝大多数能在Linux上运行的软件,例如假设Mumble语音服务器、Minecraft服务器、Apache网站服务器等等……
除此之外,还可以利用上面的GPIO,让它能够像单片机一样控制一些电子零件。
安装必要的软件包:Python、pip、CircuitPython。前面两个大家都很熟悉,最后一个CircuitPython就是一个能在Linux板子上运行类似单片机功能的框架,是由电子界著名的Adafruit出品的。
因为具体的步骤有点复杂,想要研究的可以参考官方的文档:
https://learn.adafruit.com/circuitpython-on-orangepi-linux/circuitpython-orangepi
官方文档使用的是Armbian系统以及较老的香橙派,不过大致是一样的,需要修改一些东西让CircuitPython能够识别到我们使用的板子型号。
为此,我写了一个脚本,可以一键在香橙派Ubuntu系统下安装好CircuitPython以及我们使用的OLED屏幕和WS2812 LED灯带相应的依赖库。
脚本地址:
https://raw.githubusercontent.com/RealCorebb/bbServer/master/bbServer.sh
可以使用以下方式安装:
wget https://raw.githubusercontent.co ... /master/bbServer.sh
chmod 777 bbServer.sh
./bbServer.sh
在安装好后,需要重启一次。安装好后会把bbServer的demo示例程序设为开机自启,所以重启后会看到LED灯带动画以及OLED屏幕显示时间、CPU、内存信息了。
以下是demo的程序,可以看到使用CircuiPython是比较容易上手,发挥自己的想法的。有丰富的库,能够立马上手。
1.import threading
2.import board
3.import neopixel_spi
4.from PIL import Image, ImageDraw, ImageFont
5.from adafruit_led_animation.animation.blink import Blink
6.from adafruit_led_animation.animation.sparklepulse import SparklePulse
7.from adafruit_led_animation.animation.comet import Comet
8.from adafruit_led_animation.animation.chase import Chase
9.from adafruit_led_animation.animation.pulse import Pulse
10.from adafruit_led_animation.animation.sparkle import Sparkle
11.from adafruit_led_animation.animation.rainbowchase import RainbowChase
12.from adafruit_led_animation.animation.rainbowsparkle import RainbowSparkle
13.from adafruit_led_animation.animation.rainbowcomet import RainbowComet
14.from adafruit_led_animation.animation.solid import Solid
15.from adafruit_led_animation.animation.colorcycle import ColorCycle
16.from adafruit_led_animation.animation.rainbow import Rainbow
17.from adafruit_led_animation.animation.customcolorchase import CustomColorChase
18.from adafruit_led_animation.sequence import AnimationSequence
19.from adafruit_led_animation.color import PURPLE, WHITE, AMBER, JADE, MAGENTA, ORANGE , BLACK , RED
20.import adafruit_ssd1306
21.import time
22.import psutil
23.import pathlib
24.dirPath = str(pathlib.Path(__file__).parent.resolve())
25.font = ImageFont.truetype(dirPath+'/fonts/Jorolks.ttf',24)
26.smallJorolks = ImageFont.truetype(dirPath+'/fonts/Jorolks.ttf',12)
27.seledom = ImageFont.truetype(dirPath+'/fonts/Seledom.otf',14)
28.bigSeledom = ImageFont.truetype(dirPath+'/fonts/Seledom.otf',33)
29.pixelCorebb = ImageFont.truetype(dirPath+'/fonts/PixelCorebb.ttf',15)
30.pixelCorebbBig = ImageFont.truetype(dirPath+'/fonts/PixelCorebb.ttf',14)
31.player = Image.open(dirPath+'/icons/steve24.png')
32.player16 = Image.open(dirPath+'/icons/steve16.png')
33.performance = Image.open(dirPath+'/icons/performance.png')
34.
35.
36.# ------------------- WS2812 RGB LED -_,- ------------------
37.
38.# Update to match the pin connected to your NeoPixels
39.pixel_pin = board.SPI()
40.# Update to match the number of NeoPixels you have connected
41.pixel_num = 40
42.
43.pixels = neopixel_spi.NeoPixel_SPI(pixel_pin, pixel_num, brightness=0.1, auto_write=False)
44.
45.blink = Blink(pixels, speed=0.5, color=JADE)
46.redFastBlink = Blink(pixels, speed=0.1, color=RED)
47.colorcycle = ColorCycle(pixels, speed=0.4, colors=[MAGENTA, ORANGE])
48.comet = Comet(pixels, speed=0.01, color=PURPLE, tail_length=10, bounce=True)
49.chase = Chase(pixels, speed=0.1, size=3, spacing=6, color=WHITE)
50.pulse = Pulse(pixels, speed=0.1, period=3, color=AMBER)
51.sparkle = Sparkle(pixels, speed=0.1, color=PURPLE, num_sparkles=10)
52.solid = Solid(pixels, color=JADE)
53.black = Solid(pixels, color = BLACK)
54.rainbow = Rainbow(pixels, speed=0.1, period=2)
55.sparkle_pulse = SparklePulse(pixels, speed=0.1, period=3, color=JADE)
56.rainbow_comet = RainbowComet(pixels, speed=0.05, tail_length=15, bounce=True)
57.rainbow_chase = RainbowChase(pixels, speed=0.1, size=3, spacing=2, step=8)
58.rainbow_sparkle = RainbowSparkle(pixels, speed=0.1, num_sparkles=15)
59.custom_color_chase = CustomColorChase(
60. pixels, speed=0.1, size=2, spacing=3, colors=[ORANGE, WHITE, JADE]
61.)
62.
63.
64.animations = AnimationSequence(
65. comet,
66. blink,
67. rainbow_sparkle,
68. chase,
69. pulse,
70. sparkle,
71. rainbow,
72. solid,
73. rainbow_comet,
74. sparkle_pulse,
75. rainbow_chase,
76. custom_color_chase,
77. advance_interval=5,
78. auto_clear=True,
79.)
80.
81.# ------------------------------------
82.
83.# ------------------- SSD1306/SSD1309 OLED -_,- ------------------
84.oled = None
85.
86.# Create blank image for drawing.
87.image = Image.new("1", (128, 64))
88.draw = ImageDraw.Draw(image)
89.
90.def clear():
91. global image
92. #image = Image.new("1", (oled.width, oled.height))
93. draw.rectangle((0, 0, 128, 64), fill="black")
94. oled.fill(0)
95.
96.def bootLogo():
97. draw.text((5, 20), "bbServer", fill="white", font = font)
98. oled.image(image)
99. oled.show()
100.
101.def updateInfo():
102. global oled
103. i2c = board.I2C() # uses board.SCL and board.SDA
104. attempts = 0
105. while attempts < 5:
106. try:
107. oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c)
108. break
109. except:
110. attempts += 1
111. print('OLED not found, retrying...')
112. time.sleep(1)
113.
114. # Clear display.
115. oled.fill(0)
116. oled.rotate(False)
117. oled.show()
118. clear()
119. bootLogo()
120. time.sleep(1)
121. while True:
122. cpu = int(psutil.cpu_percent(interval=1))
123. ram = int(psutil.virtual_memory().percent)
124. timeNow = time.strftime("%H:%M")
125.
126. clear()
127. draw.rounded_rectangle((2,2,28,12), radius=3, fill="white")
128. draw.text((5,3), "CPU", fill="black", font = smallJorolks)
129. draw.text((32,0), str(cpu)+"%", fill="white",font = pixelCorebb)
130. draw.rounded_rectangle((67,2,96,12), radius=3, fill="white")
131. draw.text((70,3), "RAM", fill="black", font = smallJorolks)
132. draw.text((100,0), str(ram)+"%", fill="white",font = pixelCorebb)
133. draw.text((2,24), timeNow, fill="white",font = bigSeledom)
134. oled.image(image)
135. oled.show()
136. time.sleep(1)
137.# ------------------------------------
138.
139.if __name__ == "__main__":
140. oledThread = threading.Thread(target=updateInfo)
141. oledThread.start()
142. while True:
143. time.sleep(0.01)
144. animations.animate() 复制代码
【一些可能遇到的问题】
1.在烧写系统进TF卡后,在Windows 的资源管理器里就找不到这张TF卡了。这是正常的,因为写入了Linux系统后,Windows是读不了的。所以只能在像是Balenaetcher等烧写软件中看到,如果想变回原来的样子,在这类软件中点一下格式化即可。不要以为是TF卡坏了(作者以前就以为是TF卡坏了)
2.不知道怎么连接服务器。官方的镜像中是有桌面版和服务器版的,主要的差别在于前者有图形化的操作界面,也可以用HDMI连接显示器,后者只能连网线,用其它电脑通过SSH控制。如果作为服务器使用的话,其实一般的使用场景是类似于后者的,但对于新手来说,建议安装桌面版的,更容易上手。想了解的话,可以参考云服务器、普通Linux电脑的连接方式和工具,常用免费工具的有:Xshell,通过SSH,输入IP、用户名、密码,即可连上服务器的终端控制台。Xftp,通过SSH,可以在本地电脑与服务器之间传输文件。
3.OLED屏幕不亮。请注意,在某宝上有几款2.42寸OLED屏幕模块,请确保购买的是4/5 PIN的I2C的版本,而不是7PIN的SPI版本。另外,作者有试过在几家店里买过,实际上它们是不一样的,请认准接线图上的那一款,四个孔都在板子边缘的,且是有RES引脚的,这一款非常稳定,没遇到过什么问题。另外有一款是比较高一点,无法装进3D打印外壳里的,这款没有RES引脚,并且可能板子设计有问题,经常识别不到I2C,屏幕不亮。
4.WS2812 LED不亮。这个唯一的可能性就是接线有问题了,请注意引脚定义,不要正负极接反了。
5.安装固定不合适问题。如上文所述,杜邦线需要去壳,OLED屏幕模块确保同款,散热风扇不能太高,需要使用7mm以下的,不然会撞到屏幕。
【总结】
所以,这就是bbServer的制作过程及经验分享啦。通过制作bbServer,我们可以学到:
1.如何使用单板电脑和CircuitPython来控制电子元件。
2.如何使用Linux系统和搭建自己想要的服务。
3.还可以用Python简单、快速入门单片机领域。
在制作过程中,我们会了解一些基础知识,如单板电脑和CircuitPython的使用,GPIO引脚的控制,3D打印的设计和制作等等。同时,我们也需要有耐心和耐心来调试和解决一些问题。
总之,bbServer是一个非常有趣和有用的项目,通过它我们可以学到很多东西,并且可以用它来运行自己喜欢的应用程序,通过编写代码发挥自己无限的创意。
想更了解它,请看:
开源链接:
https://github.com/RealCorebb/bbServer
视频链接:
https://www.bilibili.com/video/BV1j24y1a7Ut/