香橙派 OrangePi AI Pro 20T 接入 0.96寸 OLED 小屏幕

香橙派 OrangePi AI Pro 20T 接入 0.96寸 OLED 小屏幕

__

香橙派 OrangePi AI Pro 20T 接入 0.96寸 OLED 小屏幕

环境:OrangePi AI Pro 20T,Ubuntu 系统
屏幕:0.96寸 SSD1306 蓝黄双色 OLED,4针 IIC 接口


一、确认屏幕接口模式

本次使用的 OLED 模块支持 SPI 和 IIC 两种模式,4针版本默认已是 IIC 模式,引脚从左到右依次为:

GND  VCC  SCL  SDA

如果你的模块是7针版本,需要通过板载电阻跳线切换到 IIC 模式(短接 R1、R4、R5、R6,去掉 R2、R3)。


二、接线说明

香橙派 AI Pro 的 40pin 扩展口提供了 I2C7 接口,对应引脚如下:

OLED 引脚 功能 40pin 针脚编号 说明
GND 电源地 6号 GND
VCC 电源正 1号 3.3V
SCL 时钟线 5号 GPIO2_11 / SCL7
SDA 数据线 3号 GPIO2_12 / SDA7

⚠️ VCC 请接 3.3V(1号脚),不建议接 5V,因为香橙派 GPIO 为 3.3V 逻辑电平。

40pin 排针位置示意(从左上角开始,奇数列在左,偶数列在右):

[1] 3.3V             [2] 5V
[3] SDA7  ← 接 SDA   [4] 5V
[5] SCL7  ← 接 SCL   [6] GND   ← 接 GND
...

三、检测 OLED 是否被识别

接好线后,用 i2cdetect 扫描 I2C 总线,注意必须加 -r 参数,否则某些设备不响应:

sudo i2cdetect -y -r 7

正常情况下会在 3c 位置看到设备地址:

     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:  ...
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- --

如果全是 --,检查以下几点:

  • 接线是否正确,SDA/SCL 有没有接反
  • VCC 是否有电(接 1号脚 3.3V)
  • 屏幕是否确实是 IIC 模式

四、安装依赖库

系统使用 Miniconda 环境,安装时需要用 Miniconda 的 pip,以 root 身份安装,这样 systemd 服务以 root 运行时也能找到这些包:

sudo /usr/local/miniconda3/bin/pip install luma.oled psutil

同时将当前用户加入 i2c 组,方便手动调试时不需要 sudo:

sudo usermod -aG i2c $USER
newgrp i2c   # 临时生效,或重新登录后永久生效

五、系统监控脚本

针对 AI Pro 这块板子的特点(跑 AI 推理任务为主),脚本显示以下4行信息:

  • 第1行:CPU 使用率 + 风扇转速
  • 第2行:NPU AICore 使用率 + NPU 温度
  • 第3行:NPU 显存使用情况
  • 第4行:本机 IP 地址
# system_monitor.py
from luma.core.interface.serial import i2c
from luma.oled.device import ssd1306
from luma.core.render import canvas
import time
import psutil
import socket
import subprocess
import os
import sys

def check_oled():
    """启动时等待 OLED 就绪,最多重试5次,每次间隔5秒"""
    for attempt in range(5):
        if not os.path.exists("/dev/i2c-7"):
            print(f"第{attempt+1}次检测:i2c-7 不存在,等待5秒...")
            time.sleep(5)
            continue
        try:
            result = subprocess.run(
                ['i2cdetect', '-y', '-r', '7'],
                capture_output=True, text=True, timeout=5
            )
            if '3c' in result.stdout.lower():
                print("OLED 检测成功!")
                return
        except Exception:
            pass
        print(f"第{attempt+1}次检测:未找到 0x3C,等待5秒...")
        time.sleep(5)
    print("多次检测均未找到 OLED,退出")
    sys.exit(0)

def get_npu_info():
    """从 npu-smi 解析 NPU 温度、AICore 使用率、显存占用"""
    try:
        result = subprocess.run(['npu-smi', 'info'],
                                capture_output=True, text=True, timeout=3)
        lines = result.stdout.strip().splitlines()
        temp = aicore = mem_used = mem_total = "N/A"
        for line in lines:
            if '310B1' in line:
                parts = line.split('|')
                vals = parts[3].split()
                temp = vals[1] + "C"
            if '| 0       0' in line:
                parts = line.split('|')
                vals = parts[3].split()
                aicore = vals[0] + "%"
                mem_used = vals[1]
                mem_total = vals[3]
        return temp, aicore, mem_used, mem_total
    except:
        return "N/A", "N/A", "N/A", "N/A"

def get_fan_speed():
    """获取风扇转速占比"""
    try:
        result = subprocess.run(
            ['npu-smi', 'info', '-t', 'pwm-duty-ratio'],
            capture_output=True, text=True, timeout=3
        )
        line = result.stdout.strip()
        fan = line.split(':')[-1].strip()
        return fan + "%"
    except:
        return "N/A"

def get_ip():
    """获取本机 IP 地址"""
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        ip = s.getsockname()[0]
        s.close()
        return ip
    except:
        return "No Network"

# 启动时先检测设备
check_oled()

# 初始化 OLED
try:
    serial = i2c(port=7, address=0x3C)
    device = ssd1306(serial)
except Exception as e:
    print(f"OLED 初始化失败: {e}")
    sys.exit(0)

print("系统监控启动,Ctrl+C 退出")

try:
    while True:
        cpu_percent = psutil.cpu_percent(interval=1)
        npu_temp, aicore, mem_used, mem_total = get_npu_info()
        fan = get_fan_speed()
        ip = get_ip()

        with canvas(device) as draw:
            draw.text((0,  0), f"CPU: {cpu_percent:4.1f}%   FAN:{fan}", fill="white")
            draw.text((0, 16), f"NPU: {aicore}    Tmp:{npu_temp}", fill="white")
            draw.text((0, 32), f"MEM: {mem_used} / {mem_total} MB", fill="white")
            draw.text((0, 48), f"IP: {ip}", fill="white")

        time.sleep(3)

except KeyboardInterrupt:
    device.clear()
    print("已退出")
except Exception as e:
    print(f"运行异常: {e}")
    sys.exit(0)

将脚本保存到 /home/HwHiAiUser/system_monitor.py,手动测试运行:

python3 system_monitor.py

六、设置开机自启动

使用 systemd 管理服务,以 root 用户运行,确保 npu-smi 有足够权限。

1. 创建服务文件:

sudo nano /etc/systemd/system/oled-monitor.service

写入以下内容:

[Unit]
Description=OLED System Monitor
After=network.target

[Service]
Type=simple
User=root
ExecStartPre=/bin/sleep 10
ExecStart=/usr/local/miniconda3/bin/python3 /home/HwHiAiUser/system_monitor.py
Restart=always
RestartSec=15
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

说明:User=root 是因为 npu-smi 需要 root 权限才能读取 NPU 信息,服务本身只做系统信息展示,无安全风险。ExecStartPre=/bin/sleep 10 是为了等待开机后 i2c 总线完全初始化。

2. 启用并启动服务:

sudo systemctl daemon-reload          # 重载配置
sudo systemctl enable oled-monitor    # 设置开机自启
sudo systemctl start oled-monitor     # 立即启动
sudo systemctl status oled-monitor    # 查看运行状态

3. 常用管理命令:

sudo journalctl -u oled-monitor -f    # 实时查看日志
sudo systemctl stop oled-monitor      # 手动停止
sudo systemctl disable oled-monitor   # 取消开机自启
sudo systemctl restart oled-monitor   # 重启服务

七、OLED 未插入时的处理逻辑

脚本启动时会先扫描 /dev/i2c-7 设备,检测不到 OLED 时最多重试5次(每次间隔5秒),全部失败后静默退出。systemd 设置为 Restart=always,会在15秒后再次尝试,直到插入 OLED 为止。

场景 行为
开机时未插 OLED 每次重试5次后退出,15秒后再试,不影响系统
开机时已插 OLED 检测成功后正常显示系统信息
运行中拔掉 OLED 捕获异常退出,15秒后重启服务重新检测

八、效果说明

屏幕分辨率 128×64,默认字体行高 16px,正好显示4行内容:

CPU:12.3% FAN:35%
NPU:0%  Tmp:50C
MEM:1907/23673MB
IP: 192.168.1.100

数据来源说明:

  • CPU 使用率psutil
  • 风扇转速npu-smi info -t pwm-duty-ratio(PWM 占空比,反映风扇转速)
  • NPU 温度 / AICore 使用率 / 显存npu-smi info,适用于板载 310B1 昇腾芯片
  • IP 地址:通过 UDP socket 探测本机出口 IP

九、常见问题排查

扫描不到 OLED(i2cdetect 全是 --)

  • 检查接线,特别是 SDA/SCL 是否接反
  • 必须用 sudo i2cdetect -y -r 7,不加 -r 某些设备不响应

手动运行正常,systemd 服务启动失败

  • 检查依赖库是否以 root 身份安装:sudo /usr/local/miniconda3/bin/pip install luma.oled psutil
  • sudo /usr/local/miniconda3/bin/python3 /home/HwHiAiUser/system_monitor.py 手动以 root 跑,看具体报错

屏幕显示内容不刷新

  • 确认服务状态是 active (running) 而非 inactive
  • 查看服务日志:sudo journalctl -u oled-monitor -f

运行效果如图:

IMG_20260520_192851.jpg

目前在 OrangePi AI Pro 20T + Ubuntu 系统上测试通过。

Bazzite小黄鸭decky-lsfg-vk注意事项 2026-05-10

评论区