Raspberry Pi / Jetson - OLED Secondary Screen Tutorial
Jetson Series Main Controller
1. GPIO Pin Diagram
Use a 0.91-inch OLED to test the I2C communication function, and connect according to the following wiring:
Caution: Do not connect incorrectly or cause pin short circuits, as mistakes may lead to damage to the main board hardware!
2. I2C Test
2.1. Install Dependencies
sudo apt install -y python3-pip
sudo pip3 install smbus
sudo pip3 install Adafruit_SSD13062.2, I2C Device
During normal development, we need to find the device bus and device address where the I2C device is mounted.
(1) Query the I2C bus
Entering the following command at the end point can list all buses of the device:
i2cdetect -l(2) Query I2C device
Entering the following command at the end point can list the I2C devices under the specified bus: the I2C address corresponding to oled is 0x3c
i2cdetect -y -r *4. Check IP address and port
Use ifconfig to view IP address and port
Change the code according to the actual port
3. Experimental Effect
Create the Oled_i2c.py file
sudo gedit Oled_i2c.pyCopy and paste the following code
*#!/usr/bin/env python3*
*# coding=utf-8*
import time
import os
import sys
i2c_num = 7
if len(sys.argv) > 1:
if str(sys.argv[1]).isdigit():
i2c_num = int(sys.argv[1])
print("i2c_num=", i2c_num)
passwd = "juxi"
cmd_i2c = "sudo i2cdetect -y -r " + str(i2c_num)
os.system('echo %s | sudo -S %s' % (passwd, cmd_i2c))
import Adafruit_SSD1306 as SSD
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import subprocess
class Juxi_OLED:
def __init__(self, i2c_bus=1, debug=False):
self.__debug = debug
self.__i2c_bus = i2c_bus
self.__top = -2
self.__x = 0
self.__total_last = 0
self.__idle_last = 0
self.__str_CPU = "CPU:0%"
def __del__(self):
if self.__debug:
print("---OLED-DEL---")
* # Initialize OLED, return True on success, False on failure*
def begin(self):
try:
self.__oled = SSD.SSD1306_128_32(
rst=None, i2c_bus=self.__i2c_bus, gpio=1)
self.__oled.begin()
self.__oled.clear()
self.__oled.display()
self.__width = self.__oled.width
self.__height = self.__oled.height
self.__image = Image.new('1', (self.__width, self.__height))
self.__draw = ImageDraw.Draw(self.__image)
self.__font = ImageFont.truetype("DejaVuSansMono.ttf",8) *# ImageFont.load_default()*
if self.__debug:
print("---OLED begin ok!---")
return True
except:
# if self.__i2c_bus == 1:
# self.__i2c_bus = 8
# elif self.__i2c_bus == 8:
# self.__i2c_bus = 1
if self.__debug:
print("---OLED no found!---")
return False
* # Clear the display. Refresh =True Refresh immediately, refresh=False refresh not*
def clear(self, refresh=False):
self.__draw.rectangle(
(0, 0, self.__width, self.__height), outline=0, fill=0)
if refresh:
self.refresh()
* # Add characters. Start_x Start_y indicates the starting point. Text is the character to be added*
* # Refresh =True Refresh immediately, refresh=False refresh not*
def add_text(self, start_x, start_y, text, refresh=False):
if start_x > 128 or start_x < 0 or start_y < 0 or start_y > 32:
if self.__debug:
print("oled text: x, y input error!")
return
x = int(start_x + self.__x)
y = int(start_y + self.__top)
self.__draw.text((x, y), str(text), font=self.__font, fill=255)
if refresh:
self.refresh()
* # line=[1, 4]*
* # Write a line of character text. Refresh =True Refresh immediately, refresh=False refresh not.*
def add_line(self, text, line=1, refresh=False):
if line < 1 or line > 4:
if self.__debug:
print("oled line input error!")
return
y = int(8 * (line - 1))
self.add_text(0, y, text, refresh)
* # Refresh the OLED to display the content*
def refresh(self):
self.__oled.image(self.__image)
self.__oled.display()
* # Read the CPU usage rate*
def getCPULoadRate(self, index):
count = 10
if index == 0:
f1 = os.popen("cat /proc/stat", 'r')
stat1 = f1.readline()
data_1 = []
for i in range(count):
data_1.append(int(stat1.split(' ')[i+2]))
self.__total_last = data_1[0]+data_1[1]+data_1[2]+data_1[3] + \
data_1[4]+data_1[5]+data_1[6]+data_1[7]+data_1[8]+data_1[9]
self.__idle_last = data_1[3]
elif index == 4:
f2 = os.popen("cat /proc/stat", 'r')
stat2 = f2.readline()
data_2 = []
for i in range(count):
data_2.append(int(stat2.split(' ')[i+2]))
total_now = data_2[0]+data_2[1]+data_2[2]+data_2[3] + \
data_2[4]+data_2[5]+data_2[6]+data_2[7]+data_2[8]+data_2[9]
idle_now = data_2[3]
total = int(total_now - self.__total_last)
idle = int(idle_now - self.__idle_last)
usage = int(total - idle)
usageRate = int(float(usage / total) * 100)
self.__str_CPU = "CPU:" + str(usageRate) + "%"
self.__total_last = 0
self.__idle_last = 0
# if self.__debug:
# print(self.__str_CPU)
return self.__str_CPU
* # Read system time*
def getSystemTime(self):
cmd = "date +%H:%M:%S"
date_time = subprocess.check_output(cmd, shell=True)
str_Time = str(date_time).lstrip('b\'')
str_Time = str_Time.rstrip('\\n\'')
# print(date_time)
return str_Time
* # Read the memory usage and total memory*
def getUsagedRAM(self):
cmd = "free | awk 'NR==2{printf \"RAM:%2d%% -> %.1fGB \", 100*($2-$7)/$2, ($2/1048576.0)}'"
FreeRam = subprocess.check_output(cmd, shell=True)
str_FreeRam = str(FreeRam).lstrip('b\'')
str_FreeRam = str_FreeRam.rstrip('\'')
return str_FreeRam
* # Read free memory/total memory*
def getFreeRAM(self):
cmd = "free -h | awk 'NR==2{printf \"RAM: %.1f/%.1fGB \", $7,$2}'"
FreeRam = subprocess.check_output(cmd, shell=True)
str_FreeRam = str(FreeRam).lstrip('b\'')
str_FreeRam = str_FreeRam.rstrip('\'')
return str_FreeRam
* # Read the TF card space usage/TOTAL TF card space*
def getUsagedDisk(self):
cmd = "df -h | awk '$NF==\"/\"{printf \"SDC:%s -> %.1fGB\", $5, $2}'"
Disk = subprocess.check_output(cmd, shell=True)
str_Disk = str(Disk).lstrip('b\'')
str_Disk = str_Disk.rstrip('\'')
return str_Disk
* # Read the free TF card space/total TF card space*
def getFreeDisk(self):
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk:%.1f/%.1fGB\", $4,$2}'"
Disk = subprocess.check_output(cmd, shell=True)
str_Disk = str(Disk).lstrip('b\'')
str_Disk = str_Disk.rstrip('\'')
return str_Disk
* # Read the local IP address*
def getLocalIP(self):
ip = os.popen(
"/sbin/ifconfig enP8p1s0 | grep 'inet' | awk '{print $2}'").read()
ip = ip[0: ip.find('\n')]
if(ip == ''):
ip = os.popen(
"/sbin/ifconfig wlP1p1s0 | grep 'inet' | awk '{print $2}'").read()
ip = ip[0: ip.find('\n')]
if(ip == ''):
ip = 'x.x.x.x'
if len(ip) > 15:
ip = 'x.x.x.x'
return ip
* # Oled mainly runs functions that are called in a while loop and can be hot-pluggable*
def main_program(self):
state = False
try:
cpu_index = 0
state = self.begin()
while state:
self.clear()
str_CPU = self.getCPULoadRate(cpu_index)
str_Time = self.getSystemTime()
if cpu_index == 0:
str_FreeRAM = self.getUsagedRAM()
str_Disk = self.getUsagedDisk()
str_IP = "IPA:" + self.getLocalIP()
self.add_text(0, 0, str_CPU)
self.add_text(50, 0, str_Time)
self.add_line(str_FreeRAM, 2)
self.add_line(str_Disk, 3)
self.add_line(str_IP, 4)
# Display image.
self.refresh()
cpu_index = cpu_index + 1
if cpu_index >= 5:
cpu_index = 0
time.sleep(.1)
except:
if self.__debug:
print("!!!---OLED refresh error---!!!")
pass
if __name__ == "__main__":
try:
oled = Juxi_OLED(i2c_num, debug=True)
while True:
oled.main_program()
time.sleep(2)
except KeyboardInterrupt:
oled.clear(True)
del oled
print(" Program closed! ")
passAfter starting the program, the OLED will display system information such as system CPU usage, system time, and memory usage:
Raspberry Pi, Orange Pi
1. Turn on the I2C option
Enter the Raspberry Pi system settings, select: Preferences → Raspberry Pi Configuration → Interfaces → Find the I2C option and turn it on, then the system will restart automatically. (Refer to the figure below)
If you are in command mode, you can enter sudo raspi-config to still find the option to enable I2C and then restart the system.
2. Install the I2C library files
sudo apt install -y python3-dev
sudo apt install -y python3-smbus i2c-tools
sudo apt install -y python3-pil
sudo apt install -y python3-pip
sudo apt install -y python3-setuptools
sudo apt install -y python3-rpi.gpio
sudo apt install -y python3-venvI am using Python3 . After installing the I2C library, use the i2cdetect command to check if the display module is recognized
i2cdetect -y 1Indicates that the device has been detected at address "0x3c" (as shown below). The default for this type of device is a hexadecimal address.
3. Font File Configuration
The code uses DejaVuSansMono.ttf font:
sudo apt-get install -y fonts-dejavu-coreor modify the code to use a font already available in the system (such as ImageFont.load_default ())
4. Permission Configuration
Ensure that the I2C device file permissions are correct:
sudo chmod a+rw /dev/i2c-* # Temporary effect, permanent effect requires configuration of udev rules5. Check IP address and port
Use ifconfig to view the actual IP address and port
*# (如未安装net-tools)sudo apt install net-tools*
ifconfigChange the contents of the following two boxed areas in the code according to the actual port
6. Create the Oled_i2c.py file
Create the Oled_i2c.py file in the Home directory
*# (如未安装gedit) sudo apt install gedit*
sudo gedit Oled_i2c.pyCopy the following code, paste it into the file, save, and close
*#!/usr/bin/env python3*
*# coding=utf-8*
import time
import os
import sys
i2c_num = 7
if len(sys.argv) > 1:
if str(sys.argv[1]).isdigit():
i2c_num = int(sys.argv[1])
print("i2c_num=", i2c_num)
passwd = "juxi"
cmd_i2c = "sudo i2cdetect -y -r " + str(i2c_num)
os.system('echo %s | sudo -S %s' % (passwd, cmd_i2c))
import Adafruit_SSD1306 as SSD
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import subprocess
class Juxi_OLED:
def __init__(self, i2c_bus=1, debug=False):
self.__debug = debug
self.__i2c_bus = i2c_bus
self.__top = -2
self.__x = 0
self.__total_last = 0
self.__idle_last = 0
self.__str_CPU = "CPU:0%"
def __del__(self):
if self.__debug:
print("---OLED-DEL---")
* # 初始化OLED,成功返回:True,失败返回:False*
* # Initialize OLED, return True on success, False on failure*
def begin(self):
try:
self.__oled = SSD.SSD1306_128_32(
rst=None, i2c_bus=self.__i2c_bus, gpio=1)
self.__oled.begin()
self.__oled.clear()
self.__oled.display()
self.__width = self.__oled.width
self.__height = self.__oled.height
self.__image = Image.new('1', (self.__width, self.__height))
self.__draw = ImageDraw.Draw(self.__image)
self.__font = ImageFont.truetype("DejaVuSansMono.ttf",8) *# ImageFont.load_default()*
if self.__debug:
print("---OLED begin ok!---")
return True
except:
# if self.__i2c_bus == 1:
# self.__i2c_bus = 8
# elif self.__i2c_bus == 8:
# self.__i2c_bus = 1
if self.__debug:
print("---OLED no found!---")
return False
* # 清除显示。refresh=True立即刷新,refresh=False不刷新。*
* # Clear the display. Refresh =True Refresh immediately, refresh=False refresh not*
def clear(self, refresh=False):
self.__draw.rectangle(
(0, 0, self.__width, self.__height), outline=0, fill=0)
if refresh:
self.refresh()
* # 增加字符。start_x start_y表示开始的点。text是要增加的字符。*
* # refresh=True立即刷新,refresh=False不刷新。*
* # Add characters. Start_x Start_y indicates the starting point. Text is the character to be added*
* # Refresh =True Refresh immediately, refresh=False refresh not*
def add_text(self, start_x, start_y, text, refresh=False):
if start_x > 128 or start_x < 0 or start_y < 0 or start_y > 32:
if self.__debug:
print("oled text: x, y input error!")
return
x = int(start_x + self.__x)
y = int(start_y + self.__top)
self.__draw.text((x, y), str(text), font=self.__font, fill=255)
if refresh:
self.refresh()
* # 写入一行字符text。refresh=True立即刷新,refresh=False不刷新。*
* # line=[1, 4]*
* # Write a line of character text. Refresh =True Refresh immediately, refresh=False refresh not.*
def add_line(self, text, line=1, refresh=False):
if line < 1 or line > 4:
if self.__debug:
print("oled line input error!")
return
y = int(8 * (line - 1))
self.add_text(0, y, text, refresh)
* # 刷新OLED,显示内容*
* # Refresh the OLED to display the content*
def refresh(self):
self.__oled.image(self.__image)
self.__oled.display()
* # 读取CPU占用率*
* # Read the CPU usage rate*
def getCPULoadRate(self, index):
count = 10
if index == 0:
f1 = os.popen("cat /proc/stat", 'r')
stat1 = f1.readline()
data_1 = []
for i in range(count):
data_1.append(int(stat1.split(' ')[i+2]))
self.__total_last = data_1[0]+data_1[1]+data_1[2]+data_1[3] + \
data_1[4]+data_1[5]+data_1[6]+data_1[7]+data_1[8]+data_1[9]
self.__idle_last = data_1[3]
elif index == 4:
f2 = os.popen("cat /proc/stat", 'r')
stat2 = f2.readline()
data_2 = []
for i in range(count):
data_2.append(int(stat2.split(' ')[i+2]))
total_now = data_2[0]+data_2[1]+data_2[2]+data_2[3] + \
data_2[4]+data_2[5]+data_2[6]+data_2[7]+data_2[8]+data_2[9]
idle_now = data_2[3]
total = int(total_now - self.__total_last)
idle = int(idle_now - self.__idle_last)
usage = int(total - idle)
usageRate = int(float(usage / total) * 100)
self.__str_CPU = "CPU:" + str(usageRate) + "%"
self.__total_last = 0
self.__idle_last = 0
# if self.__debug:
# print(self.__str_CPU)
return self.__str_CPU
* # 读取系统时间*
* # Read system time*
def getSystemTime(self):
cmd = "date +%H:%M:%S"
date_time = subprocess.check_output(cmd, shell=True)
str_Time = str(date_time).lstrip('b\'')
str_Time = str_Time.rstrip('\\n\'')
# print(date_time)
return str_Time
* # 读取内存占用率 和 总内存*
* # Read the memory usage and total memory*
def getUsagedRAM(self):
cmd = "free | awk 'NR==2{printf \"RAM:%2d%% -> %.1fGB \", 100*($2-$7)/$2, ($2/1048576.0)}'"
FreeRam = subprocess.check_output(cmd, shell=True)
str_FreeRam = str(FreeRam).lstrip('b\'')
str_FreeRam = str_FreeRam.rstrip('\'')
return str_FreeRam
* # 读取空闲的内存 / 总内存*
* # Read free memory/total memory*
def getFreeRAM(self):
cmd = "free -h | awk 'NR==2{printf \"RAM: %.1f/%.1fGB \", $7,$2}'"
FreeRam = subprocess.check_output(cmd, shell=True)
str_FreeRam = str(FreeRam).lstrip('b\'')
str_FreeRam = str_FreeRam.rstrip('\'')
return str_FreeRam
* # 读取TF卡空间占用率 / TF卡总空间*
* # Read the TF card space usage/TOTAL TF card space*
def getUsagedDisk(self):
cmd = "df -h | awk '$NF==\"/\"{printf \"SDC:%s -> %.1fGB\", $5, $2}'"
Disk = subprocess.check_output(cmd, shell=True)
str_Disk = str(Disk).lstrip('b\'')
str_Disk = str_Disk.rstrip('\'')
return str_Disk
* # 读取空闲的TF卡空间 / TF卡总空间*
* # Read the free TF card space/total TF card space*
def getFreeDisk(self):
cmd = "df -h | awk '$NF==\"/\"{printf \"Disk:%.1f/%.1fGB\", $4,$2}'"
Disk = subprocess.check_output(cmd, shell=True)
str_Disk = str(Disk).lstrip('b\'')
str_Disk = str_Disk.rstrip('\'')
return str_Disk
* # 获取本机IP*
* # Read the local IP address*
def getLocalIP(self):
ip = os.popen(
"/sbin/ifconfig enP8p1s0 | grep 'inet' | awk '{print $2}'").read()
ip = ip[0: ip.find('\n')]
if(ip == ''):
ip = os.popen(
"/sbin/ifconfig wlP1p1s0 | grep 'inet' | awk '{print $2}'").read()
ip = ip[0: ip.find('\n')]
if(ip == ''):
ip = 'x.x.x.x'
if len(ip) > 15:
ip = 'x.x.x.x'
return ip
* # oled主要运行函数,在while循环里调用,可实现热插拔功能。*
* # Oled mainly runs functions that are called in a while loop and can be hot-pluggable*
def main_program(self):
state = False
try:
cpu_index = 0
state = self.begin()
while state:
self.clear()
str_CPU = self.getCPULoadRate(cpu_index)
str_Time = self.getSystemTime()
if cpu_index == 0:
str_FreeRAM = self.getUsagedRAM()
str_Disk = self.getUsagedDisk()
str_IP = "IPA:" + self.getLocalIP()
self.add_text(0, 0, str_CPU)
self.add_text(50, 0, str_Time)
self.add_line(str_FreeRAM, 2)
self.add_line(str_Disk, 3)
self.add_line(str_IP, 4)
# Display image.
self.refresh()
cpu_index = cpu_index + 1
if cpu_index >= 5:
cpu_index = 0
time.sleep(.1)
except:
if self.__debug:
print("!!!---OLED refresh error---!!!")
pass
if __name__ == "__main__":
try:
oled = Juxi_OLED(i2c_num, debug=True)
while True:
oled.main_program()
time.sleep(2)
except KeyboardInterrupt:
oled.clear(True)
del oled
print(" Program closed! ")
pass7. Create a virtual environment
# 安装虚拟环境工具(如果未安装)sudo apt-get install -y python3-venv
# 创建虚拟环境
python3 -m venv oled_env
# 激活虚拟环境source oled_env/bin/activate # 激活后命令行前会显示 (oled_env)# 在虚拟环境中安装依赖
# 使用阿里云镜像安装
pip install Adafruit_SSD1306 -i https://mirrors.aliyun.com/pypi/simple/
# 同时安装 pillow(依赖库)
pip install pillow -i https://mirrors.aliyun.com/pypi/simple/
# 运行代码(需在虚拟环境中)
# 进入到对应的simple文件夹下
cd /home/pi/oled-screen/luma.examples/examples
python3 oled_i2c.py # 默认总线 7
# 或指定总线(如总线 1)
python3 oled_i2c.py 1
#若提示权限错误,检查 sudo 配置或 I2C 设备权限