目录
- 背景
- 实验环境
- 问题记录
- 1、`sudo pip install xxx`报错:error: externally-managed-environment
- 2、`sudo apt-get install python-smbus`报错:E: Package 'python-smbus' has no installation candidate
- 3、`pip install`执行缓慢或中断失败
- 4、使用Pillow库报错:ImportError: cannot import name '_imagingft' from 'PIL'
- 5、使用Pillow库报错:ImportError: The _imagingft C module is not installed
- 点亮效果
- 点亮脚本记录
背景
几年前买的树莓派3B+,吃灰了一段时间,拿出来玩一玩。把系统版本更新到了目前最新的 raspios bookworm(Debian 12)版本,旧版系统上有正常点亮的SSD1306驱动128x64 oled屏和python程序,准备恢复点亮的,结果遇到各种问题,就记录总结一下。
实验环境
时间:2024年8月25日
硬件:树莓派3B+
系统版本:Raspberry Pi OS Lite(32位) bookworm
问题记录
2019年,当时参考 树莓派实验室 - 在树莓派上使用 SSD1306 OLED 屏幕 这篇教程,在低版本系统上成功点亮。
这次,操作步骤还是参考上面的那片教程,但随着Debian系统和python版本更新,有些地方不再适用了。
1、sudo pip install xxx
报错:error: externally-managed-environment
具体错误输出如下:
$ sudo python -m pip install --upgrade pip setuptools wheel
error: externally-managed-environment× This environment is externally managed
╰─> To install Python packages system-wide, try apt installpython3-xyz, where xyz is the package you are trying toinstall.If you wish to install a non-Debian-packaged Python package,create a virtual environment using python3 -m venv path/to/venv.Then use path/to/venv/bin/python and path/to/venv/bin/pip. Makesure you have python3-full installed.For more information visit http://rptl.io/venvnote: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
解决方法: Raspberry Pi OS系统版本升级后,包管理优化的一些改变。按照提示,配置 python 虚拟环境(venv
)即可,上面错误信息中就有链接,教程网上也随便能搜到,不再赘述。
为了方便以后使用,我在.bashrc最后添加了激活 python 虚拟环境的命令,这样就每次 ssh 打开的远程终端都默认是虚拟环境:
~/.bashrc:
# 省略默认内容...
source /home/xxuser/rpi-py-env/bin/activate
2、sudo apt-get install python-smbus
报错:E: Package ‘python-smbus’ has no installation candidate
解决方法: 新版本系统中,通过apt安装python模块的方式不再适用,通过pip install smbus
方式安装,其他python中用到的依赖库同理。
pip install smbus
# python中用到的其他依赖模块...
3、pip install
执行缓慢或中断失败
**解决方法:**更换国内pip镜像源,参考配置:
/home/xxuser/.config/pip/pip.conf
[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
extra-index-url=https://mirrors.aliyun.com/pypi/simple/https://pypi.mirrors.ustc.edu.cn/simple/[install]
trusted-host=pypi.tuna.tsinghua.edu.cnmirrors.aliyun.compypi.mirrors.ustc.edu.cn
4、使用Pillow库报错:ImportError: cannot import name ‘_imagingft’ from ‘PIL’
**解决方法:**2024年8月这个时间,pip install Pillow
默认安装的最新版本是pillow 10.4.0,pillow版本和python版本不兼容会报错,经过逐一验证,回退安装低版本Pillow-8.4.0,避免该报错,但是有其他错误,下面会说到。
pip uninstall Pillow
pip install "Pillow<9.0.0"
5、使用Pillow库报错:ImportError: The _imagingft C module is not installed
解决方法: 先卸载Pillow,安装必要的依赖库,再加上--no-cache-dir
参数重新安装Pillow
pip uninstall Pillow
sudo apt-get install libjpeg-dev zlib1g-dev
pip install "Pillow<9.0.0" --no-cache-dir
点亮效果
点亮脚本记录
基于 Adafruit_Python_SSD1306/example/stats.py 进行的定制修改。
# Copyright (c) 2017 Adafruit Industries
# Author: Tony DiCola & James DeVito
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.# 指定虚拟环境的路径
import sys
sys.path.insert(0, '/home.shenyong/rpi-py-env/lib/python3.11/site-packages')import timeimport Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306from PIL import Image
from PIL import ImageDraw
from PIL import ImageFontimport subprocess
import psutil######## for DHT11 ########
import smbus
import sys
import threading
import Adafruit_DHTDHT_PIN = 4
SAMPLE_INTERVAL_SEC = 5
init_ok = False
shared_lock = threading.Lock()
envStr = ""
######## for DHT11 end ######### Raspberry Pi pin configuration:
RST = None # on the PiOLED this pin isnt used
# Note the following are only used with SPI:
DC = 23
SPI_PORT = 0
SPI_DEVICE = 0# Beaglebone Black pin configuration:
# RST = 'P9_12'
# Note the following are only used with SPI:
# DC = 'P9_15'
# SPI_PORT = 1
# SPI_DEVICE = 0# 128x32 display with hardware I2C:
#disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST)# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)# Note you can change the I2C address by passing an i2c_address parameter like:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, i2c_address=0x3C)# Alternatively you can specify an explicit I2C bus number, for example
# with the 128x32 display you would use:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, i2c_bus=2)# 128x32 display with hardware SPI:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))# 128x64 display with hardware SPI:
# disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST, dc=DC, spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE, max_speed_hz=8000000))# Alternatively you can specify a software SPI implementation by providing
# digital GPIO pin numbers for all the required display pins. For example
# on a Raspberry Pi with the 128x32 display you might use:
# disp = Adafruit_SSD1306.SSD1306_128_32(rst=RST, dc=DC, sclk=18, din=25, cs=22)def get_dht11_sensor():global init_okglobal shared_lockglobal dispglobal envStrwhile True:humidity, temperature = Adafruit_DHT.read_retry(Adafruit_DHT.DHT11, DHT_PIN)init_ok = Trueshared_lock.acquire()envStr = 'T:{:.0f}°C H:{:.0f}% '.format(temperature, humidity)shared_lock.release()time.sleep(SAMPLE_INTERVAL_SEC)if __name__ == '__main__':now = time.strftime('%m/%d %H:%M:%S', time.localtime(time.time()))# Initialize library.disp.begin()# Clear display.disp.clear()disp.display()# Create blank image for drawing.# Make sure to create image with mode '1' for 1-bit color.width = disp.widthheight = disp.heightimage = Image.new('1', (width, height))# Get drawing object to draw on image.draw = ImageDraw.Draw(image)# Draw a black filled box to clear the image.draw.rectangle((0,0,width,height), outline=0, fill=0)# Draw some shapes.# First define some constants to allow easy resizing of shapes.padding = 0top = paddingbottom = height-padding# Move left to right keeping track of the current x position for drawing shapes.x = 0# Load default font.# font = ImageFont.load_default()font_size = 12font = ImageFont.truetype('/home/shenyong/Adafruit_Python_SSD1306/examples/JetBrainsMono-Thin.ttf', font_size)# font = ImageFont.truetype('/home/shenyong/Adafruit_Python_SSD1306/examples/JetBrainsMono-Light.ttf', font_size)# font = ImageFont.truetype('/home/shenyong/Adafruit_Python_SSD1306/examples/JetBrainsMono-Medium.ttf', font_size)# 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('Minecraftia.ttf', 8)rpi_logo = Image.open('/home/shenyong/Adafruit_Python_SSD1306/examples/rpi.ppm').convert('1')# draw.text((0, top), "Raspberry Pi 3B+", font=font, fill=255)# draw.text((0, top + font_size + 1), "Initializing...", font=font, fill=255)disp.image(rpi_logo)disp.display()try:threading.Thread(target=get_dht11_sensor).start()except:print("Error: unable to start thread")lastIdle = 0lastTotal = 0while True:if (init_ok):# Draw a black filled box to clear the image.draw.rectangle((0,0,width,height), outline=0, fill=0)# Shell scripts for system monitoring from here : https://unix.stackexchange.com/questions/119126/command-to-display-memory-usage-disk-usage-and-cpu-loadcmd = "hostname -I | cut -d\' \' -f1"IPDesc = subprocess.check_output(cmd, shell = True )IP = "IP: " + str(IPDesc, encoding = "utf-8")cpu_percent = psutil.cpu_percent(interval=1)CPU = 'CPU: {:.1f}%'.format(cpu_percent)# cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%sMB %.2f%%\", $3,$2,$3*100/$2 }'"#cmd = "free -m | awk 'NR==2{printf \"Mem: %s/%sMB %d%\", $3,$2,$3*100/$2 }'"#MemUsage = subprocess.check_output(cmd, shell = True )mem_info = psutil.virtual_memory()memTotalMB = mem_info.total/1024/1024memUsedMB = (mem_info.total - mem_info.available)/1024/1024MemUsage = 'Mem: {:d}M/{:d}M {:d}%'.format(int(memUsedMB), int(memTotalMB), int(mem_info.percent))cmd = "df -h / | awk 'NR==2{print $3\"/\"$2\" \"$5}'"diskDesc = subprocess.check_output(cmd, shell = True )Disk = 'Disk: {}'.format(str(diskDesc, encoding = "utf-8"))# disk_usage = psutil.disk_usage('/')# diskTotalGB = disk_usage.total/1024/1024/1024# diskUsedGB = disk_usage.used/1024/1024/1024# Disk = 'Disk: {:.1f}/{:d}GB {}%'.format(diskUsedGB, int(diskTotalGB), int(disk_usage.percent))nowTime = time.strftime('%H:%M', time.localtime(time.time()))# Write text.line = 0draw.text((x, top + font_size * line + 1), IP, font=font, fill=255)line += 1draw.text((x, top + font_size * line + 1), CPU, font=font, fill=255)line += 1draw.text((x, top + font_size * line + 1), MemUsage, font=font, fill=255)line += 1draw.text((x, top + font_size * line + 1), Disk, font=font, fill=255)shared_lock.acquire()line += 1lineStr = '{} {}'.format(nowTime, envStr)draw.text((0, top + font_size * line + 1), lineStr, font=font, fill=255)shared_lock.release()disp.image(image)disp.display()time.sleep(2)