0%

树莓派0.96寸oled显示系统信息

github:https://github.com/maplesugarr/raspberrypi-ssd1306_0.96_oled

网上花了12买的一块屏幕,比较小。选的是黄蓝双色,我以为是可以显示黄色或者蓝色,实际上是上面8个像素点是黄色,下面是蓝色。

并不知道怎玩耍,找到一片博客Interfacing SSD1306 OLED Display with Raspberry Pi:https://circuitdigest.com/microcontroller-projects/ssd1306-oled-display-with-raspberry-pi 。它使用库Adafruit_SSD1306:https://github.com/adafruit/Adafruit_Python_SSD1306

Adafruit_SSD1306库的使用

下载Adafruit_SSD1306后,可以在examples文件夹找到各种例子。

创建一个和屏幕一样大小的画布

1
2
3
4
5
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))

拿到画笔

1
2
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)

清空画布

1
2
# Draw a black filled box to clear the image.
draw.rectangle((0,0,width,height), outline=0, fill=0)

用画笔在画布上写字,或者画图像,甚至还封装了一些形状。

1
2
3
draw.text((x, top),  "IP: " + str(IP),  font=font, fill=255)
disp.image(image)
draw.rectangle((x, top, x+shape_width, bottom), outline=255, fill=0)

接线

这两张图是对应的

oled屏幕连接树莓派的i2c1_sda,i2c1_scl。

打开树莓派i2c

执行命令raspi-config,找到Interfacing Options,找到i2c打开。

I2C是一种总线式结构,它只需要SCL时钟信号线与SDA数据线,两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为常用的通信方式。
由于I2C采用的是主从式通信方式,所以,通信的过程完全由主设备仲裁。在通信之前,必须由主设备发送一个起始信号,决定数据是否可以开始传送,并且在结束通信时,必须再由主设备发送一个结束信号,以表示通信已经结束。

由于I2C是主从式通信,也就意味着一根总线上可以挂载多个从设备,那么主设备如何区分这些从设备呢?主设备如何知道是在与哪一个从设备在通信呢?答案是:通过地址。每一个从设备都有自己的地址编码,也就是说,主设备在与具体的某一个从设备通信之前,必须先发送地址,以表示与主设备通信的是该设备。

由于只有一个i2c设备,还有库,使用默认就好了,不需要详细了解i2c了。要是以后使用多个i2c再回来看

安装相关库(python3)

apt-get -y install build-essential python3-dev python3-pip
apt-get -y install python3-rpi.gpio
pip3 install RPi.GPIO
apt-get -y install python3-pil python3-smbus #System Management Bus (I2C bus)

安装Adafruit SSD1306(python3)

apt-get -y install git-core #安装git
git clone https://github.com/adafruit/Adafruit_Python_SSD1306
cd Adafruit_Python_SSD1306
sudo python3 setup.py install
cd Adafruit_Python_SSD1306/examples #有各种例子

显示任意大小的文字

我选择的是128x64分辨率,默认显示8行文字,显示的字实在是太小了。想再这种高分辨下一行字符占用两行,也就是总共显示4行,怎么办?

1
2
# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)

在画笔在画布上写文字时候,使用了font参数,如果把fontsize相应调大,就能实现一行文字占用两行显示。

1
2
3
# Load default font.
font = ImageFont.load_default()
draw.text((x, top), "IP: " + str(IP), font=font, fill=255)

下面是写好的方法,只要传入想用几行lines显示一行文字,就能得到对应的fontsize。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
''' 计算让文字占多少行的fontsize '''
def get_fontsize(self,lines):
'''显示任意大小的文字,只要把fontsize对应设置好,缩放字体。draw.text((0, -2), 'abc', font=font, fill=255)就行了。'''
'''显示占两行的文字。128x64分辨率下,可以显示八行文字。把缩放分数设置为高度的0.25,就是占了两行。'''
txt = "test"
fontsize = 1 # starting font size

# portion of image width you want text width to be
img_fraction = 0.125*lines
# Alternatively load a TTF font. Make sure the .ttf font file is in the same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
font = ImageFont.truetype(ARIAL_FONT_FILE, fontsize)
''' font.getsize(txt) 得到字体大小的数组,[宽,高]'''
''' self.image就是画布 '''
while font.getsize(txt)[1] < img_fraction*self.image.size[1]:#高度比较
# iterate until the text size is just larger than the criteria
fontsize += 1
font = ImageFont.truetype(ARIAL_FONT_FILE, fontsize)
# optionally de-increment to be sure it is less than criteria
#fontsize = -1 这里测试不用
''' 返回字体大小和高度 '''
return fontsize,font.getsize(txt)[1]

显示自定义图片

屏幕只有亮暗两种,所以只能显示这种黑白照片。

使用ps制作好,使用程序保存起来。

1
2
3
4
5
6
''' Alternatively load a different format image, resize it, and convert to 1 bit color. '''
image = Image.open('raspi-off.png').resize((disp.width, disp.height), Image.ANTIALIAS).convert('1')
''' 为了不每次都resize和convert,把处理好的图像保存起来。 '''
image.save('demo.png')
# Display image.
disp.image(image)

按钮和菜单

使用两个按钮,一个是选择菜单项,一个是确定执行。

添加按钮监听

1
2
3
4
5
6
7
8
9
def button_listening(self):
RPi.GPIO.setmode(RPi.GPIO.BCM)
''' 按钮GPIO17,GPIO27 '''
RPi.GPIO.setup(17, RPi.GPIO.IN, pull_up_down=RPi.GPIO.PUD_UP)
RPi.GPIO.setup(27, RPi.GPIO.IN, pull_up_down=RPi.GPIO.PUD_UP)
''' 软件去抖,当检测到上升沿后,进入这个中断,在延时200ms中忽略上升沿。 实际测试,设置1000ms,可以消除连续执行两次的抖动。'''
''' add rising edge detection on a channel, ignoring further edges for 200ms for switch bounce handling '''
RPi.GPIO.add_event_detect(17, RPi.GPIO.RISING, callback=self.button_event, bouncetime=1000)
RPi.GPIO.add_event_detect(27, RPi.GPIO.RISING, callback=self.button_event, bouncetime=1000)

根据按钮的GPIO号码,实现选择菜单和确定功能

1
2
3
4
5
6
7
8
9
''' 自动传入GPIO pin '''
def button_event(self, channel):
print("RISING " + str(channel))
''' 蜂鸣器发声 '''
beepAction(0.5,0.1,1)
if channel==17 and self.status == DISP_STATUS.menu: ...
if channel==17 and self.status == DISP_STATUS.auto: ...
if channel==27 and self.status == DISP_STATUS.menu: ...
if (channel==17 or channel==27) and self.status == DISP_STATUS.sleep: ...

其他:显示系统信息,睡眠唤醒