使用树莓派 结合Python Adafruit驱动OLED屏幕 显示实时视频

news2024/11/14 13:48:48

 关于OLED屏幕的驱动,在之前我已经写过很多篇博文:

IIC 协议 和 OLED_oled iic-CSDN博客

香橙派配合IIC驱动OLED & 使用SourceInsight解读源码_香橙派5 驱动屏幕-CSDN博客

这两篇博文都是通过模拟或调用IIC协议来使用C语言驱动OLED屏幕,现在在树莓派中我尝试了和香橙派类似的方法,我发现了一些问题:香橙派中使用了wiringPi库提供的Oled测试代码并进行修改,其中包含了“font.h”和“oled.h”这两个头文件,但是我已经忘记了我当时这两个头文件是从哪里来的= =,如果不使用这两个库,在查阅了一些别人的实现后发现,如果纯用C语言结合wiringPi库来驱动OLED本质还是要模拟IIC协议,比较麻烦。

综上,经过一些比较和考量,我决定使用Python Adafruit驱动OLED屏幕

树莓派的IIC开启和OLED屏幕识别

虽说要使用Python来驱动,但是OLED屏幕底层依然是IIC协议,所以对于树莓派需要先手动开启IIC协议:

IIC开启

  • 运行以下代码进入设置界面:
sudo raspi-config
  • 选择“Interface Options”:

  • 选择“I2c”然后打开:

此时,就可以正常使用IIC了,通过“gpio-readall” 可知:

现在,物理引脚3和5对应的就是SDA.1和SCL.1,其中“.1”代表的就是驱动文件是“/dev/i2c-1

此时,就可以按照引脚连接OLED屏幕了!

OLED屏幕识别

屏幕连接完成后,进行设备的识别:

  • 运行以下命令安装i2c-tools
sudo apt-get install -y i2c-tools    //安装IIC工具
  • 运行以下命令识别OLED屏幕
sudo i2cdetect -y 1
//“1”代表“i2c-1”

 

可见,树莓派已经成功识别到了OLED屏幕!

Adafruit_Python_SSD1306 库的下载和使用

根据淘宝的搜索,可以了解到我的OLED屏幕驱动芯片为SSD1306(这也是市面上绝大部分单片机小OLED屏幕的芯片,不过现在最新的好像换代了)。

驱动芯片为SSD1306对应的python库就是“Adafruit_Python_SSD1306"。

以下是安装的代码:

1. pip install Adafruit-GPIO
2. pip install Adafruit-SSD1306
3. git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git

安装完成后,运行例程查看效果: 

cd Adafruit_Python_SSD1306/examples/
python3 shapes.py

将shapes.py中的“disp = Adafruit_SSD1306_128_32(rst=RST)”修改为“disp = Adafruit_SSD1306_128_64(rst=RST)”,可以显示得更清晰,具体根据不同的OLED屏幕尺寸来修改

成功显示!

修改例程显示图片

 首先,将刚刚测试成功的例程“shapes.py”复制到我的代码路径中:

 cp shapes.py /home/pi/mjm_code/oled_camera.py

由于之后我还是想用C语言来调用这个python文件,所以和我之前博文的思路一样,先将不需要的代码删去,再将python程序定义为函数进行测试:

oled_camera.py: 

由于我的最终目的是显示实时的视频流,所以我先将这个python代码修改,显示一张图片,后续C语言进行while循环不断来显示当前拍照图片就可以有视频的效果。

# Copyright (c) 2014 Adafruit Industries
# Author: Tony DiCola
#
# 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 time

import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306

from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont


def init():
    # Raspberry Pi pin configuration:
    RST = 24
    # 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_64(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)

# 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.width
#    height = disp.height
#    image = 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)


# Load default font.
#    font = ImageFont.load_default()

def display():
    # Raspberry Pi pin configuration:
    RST = 24
    # Note the following are only used with SPI:
    DC = 23
    SPI_PORT = 0
    SPI_DEVICE = 0

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


# Initialize library.
    disp.begin()


    #打开本地的照片
    #当前路径测试用,需按照实际情况修改
    img = Image.open('/home/pi/mjm_code/mjm.png')

    # 调整图片大小到 128x64
    img_resized = img.resize((128, 64),Image.LANCZOS)

    image = img_resized.convert('1')

# Display image.
    disp.image(image)
    disp.display()

if __name__ == '__main__': #写一个main调用face_detect函数来测试
    init()
    display()

 显示效果

运行“python3 oled_camera.py”:

可见,虽然画面很粗糙,但是成功显示图片!而且由于使用了现成的python库,不需要在麻烦的使用IIC协议写命令,初始化,取模了。 

OLED显示实时视频流

现在已经能在OLED上显示一张图片了,再次重复刚刚的思路:

“由于我的最终目的是显示实时的视频流,所以我先将这个python代码修改,显示一张图片,后续C语言进行while循环不断来显示当前拍照图片就可以有视频的效果。”

关于如何拍照并保存本地,和如何使用C语言调用Python,可以看我之前的博文:

树莓派接入USB摄像头并使用fswebcam和mjpg-streamer进行测试_树莓派mjpg-streamer-CSDN博客

基于阿里云平台 通过树莓派实现 1:1人脸识别-CSDN博客 

编写C语言调用oled_camera.py

回顾“C语言调用Python的步骤”:

oled_show.c:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>
 
#include "oled_show.h"
 
 
void oled_init(void)
{
    Py_Initialize();
    PyObject *sys = PyImport_ImportModule("sys");
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyUnicode_FromString("."));
}
 
void oled_final(void)
{
    Py_Finalize();
}

void oled_show_init(void) //由于没有返回参数,所以可以直接定义void型
{
    PyObject *pModule = PyImport_ImportModule("oled_camera"); //加载python文件
    if (!pModule)
    {
        PyErr_Print();
        printf("Error: failed to load module\n");
        goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE
    }
    PyObject *pFunc = PyObject_GetAttrString(pModule, "init"); //加载python文件中的对应函数
    if (!pFunc)
    {
        PyErr_Print();
        printf("Error: failed to load function\n");
        goto FAILED_FUNC;
    }
    PyObject *pValue = PyObject_CallObject(pFunc, NULL);
    if (!pValue)
    {
        PyErr_Print();
        printf("Error: function call failed\n");
        goto FAILED_VALUE;
    }

    /* 没有返回值,无需调用
    int result = 0;
    if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示
    {
        PyErr_Print();
        printf("Error: parse failed");
        goto FAILED_RESULT;
    }*/
 
FAILED_RESULT:
    Py_DECREF(pValue);
FAILED_VALUE:
    Py_DECREF(pFunc);
FAILED_FUNC:
    Py_DECREF(pModule);
FAILED_MODULE:
    //无需return
}

void oled_show(void) //由于没有返回参数,所以可以直接定义void型
{
    PyObject *pModule = PyImport_ImportModule("oled_camera"); //加载python文件
    if (!pModule)
    {
        PyErr_Print();
        printf("Error: failed to load module\n");
        goto FAILED_MODULE; //goto的意思就是如果运行到这里就直接跳转到FAILED_MODULE
    }
    PyObject *pFunc = PyObject_GetAttrString(pModule, "display"); //加载python文件中的对应函数
    if (!pFunc)
    {
        PyErr_Print();
        printf("Error: failed to load function\n");
        goto FAILED_FUNC;
    }
    PyObject *pValue = PyObject_CallObject(pFunc, NULL);
    if (!pValue)
    {
        PyErr_Print();
        printf("Error: function call failed\n");
        goto FAILED_VALUE;
    }

    /* 没有返回值,无需调用
    int result = 0;
    if (!PyArg_Parse(pValue, "i", &result)) //ace_detect函数返回的是已经经过提取和取证过的置信度score,是个int型,用‘i’表示
    {
        PyErr_Print();
        printf("Error: parse failed");
        goto FAILED_RESULT;
    }*/
 
FAILED_RESULT:
    Py_DECREF(pValue);
FAILED_VALUE:
    Py_DECREF(pFunc);
FAILED_FUNC:
    Py_DECREF(pModule);
FAILED_MODULE:
    //无需return
}
oled_show.h:
#ifndef __oled__H
#define __oled__H
 
void oled_init(void);
void oled_final(void);
void oled_show_init(void);
void oled_show(void);
 
#endif

编写C程序调用刚刚的C函数实现实时视频流显示

test_oled_camera.c:

注意,由于此处涉及了拍照,拍照保存的路径和照片的名字需要和oled_camera.py中保持一样,所以此处需要按照需求修改python代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wiringPi.h>

#include "oled_show.h"

int main()
{
	oled_init();
	oled_show_init();

	while(1){
		system("wget http://192.168.2.56:8080/?action=snapshot -O /home/pi/mjm_code/oled_pic.png"); //拍照
		delay(10);//给一点时间让照片拍出来
		if(0 == access("/home/pi/mjm_code/oled_pic.png", F_OK)){ //如果照片成功拍到了
			oled_show();
		}else{
			printf("pic not exist!\n");
		}
		remove("/home/pi/mjm_code/oled_pic.png");
		delay(50); //此处决定帧率,delay时间越短帧率越高
	}

	oled_final();
	return 0;
}

编译运行

  • 输入以下指令编译:
gcc test_oled_camera.c oled_show.c -I /usr/include/python3.11/ -l python3.11 -lwiringPi
  • 输入以下指令运行程序: 
./a.out
  • 输入以下指令结束运行:
ps -ef|grep 程序名称
kill 进程编号

最终效果

视频中我出现在画框中并朝着屏幕挥手:

可见,虽然帧率和分辨率低的可怕,但是的确实现了使用OLED实时显示视频流的功能!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1527854.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Sentinel加密锁的工作原理

Sentinel加密锁是一种先进的安全机制&#xff0c;它旨在提供强大的数据加密和访问控制功能&#xff0c;确保数据在传输和存储过程中的机密性、完整性和可用性。下面将详细介绍Sentinel加密锁的工作原理、优势以及在现实中的应用。 一、Sentinel加密锁的工作原理 Sentinel加密锁…

2024年腾讯云GPU服务器价格表_1小时费用_一个月价格和一年优惠

腾讯云GPU服务器怎么收费&#xff1f;GPU服务器1小时多少钱&#xff1f;一个月收费价格表和一年费用标准&#xff0c;腾讯云百科txybk.com分享腾讯云GPU服务器GPU计算型GN10Xp、GPU服务器GN7、GPU渲染型 GN7vw等GPU实例费用价格&#xff0c;以及NVIDIA Tesla T4 GPU卡和V100详细…

LiveGBS流媒体平台GB/T28181常见问题-如何订阅设备状态在线离线状态redis订阅设备或是通道状态subscribe device操作及示例

LiveGBS如何订阅设备状态在线离线状态redis订阅设备或是通道状态subscribe device操作及示例 1、如何监听设备状态2、device订阅2.1、设备上线消息2.2、设备离线消息2.2、通道上线消息2.2、通道离线消息 3、订阅示例3.1、连接REDIS3.2、订阅device示例3.3、设备上线示例3.3.1、…

解决微信录像帧率不足30fps

问题现象 使用工具检测录像帧率不足30fps 问题分析 1&#xff0c;抓取微信录像systrace 很明显camera provider很多线程处于Runnable状态&#xff0c;获取不到cpu原因&#xff0c;有两种可能原因&#xff1a;一是cpu频率很低&#xff1b;二是存在高负载应用。 先检查cpu频率…

使用RabbitMQ,关键点总结

文章目录 1.MQ的基本概念2.常见的MQ产品3.MQ 的优势和劣势3.1 优势3.2 劣势 4.RabbitMQ简介4.1RabbitMQ 中的相关概念 1.MQ的基本概念 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进行通信。…

【c++】内联-引用-重载

主页&#xff1a;醋溜马桶圈-CSDN博客 专栏&#xff1a;c_醋溜马桶圈的博客-CSDN博客 gitee&#xff1a;mnxcc (mnxcc) - Gitee.com 目录 1.【c】内联函数 1.1 背景 1.2 内联函数的概念 1.3 内联函数的特性 1.4 宏和内联的小知识 宏的优缺点&#xff1f; C有哪些技术替代…

HTML静态网页成品作业(HTML+CSS)——宠物狗店网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

vue元素声明之后未使用的解决方法

错误情况&#xff1a;类似的这种元素声明未使用的情况 解决方法 方法一 将lintOnSave :false 改为lintOnSave:true 方法二 在项目中找到package.json........① 使用快捷键Ctrl F 搜索eslintconfig........② 找到eslintconfig..........③ 找到rules .........④ 添…

python农产品农药商城溯源系统vue+Django_flask

后端&#xff1a;python 前端&#xff1a;vue.jselementui 框架&#xff1a;django/flask Python版本&#xff1a;python3.7 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat 开发软件&#xff1a;PyCharm 使用Navicat或者其它工具&#xff0c;在mysql中创建对应名称…

嵌入式学习第二十九天!(数据结构的概念、单向链表)

数据结构&#xff1a; 1. 定义&#xff1a; 一组用来保存一种或者多种特定关系的数据的集合&#xff08;组织和存储数据&#xff09; 1. 程序设计&#xff1a; 将现实中大量而复杂的问题以特定的数据类型和特定的数据结构存储在内存中&#xff0c;并在此基础上实现某个特定的功…

CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记

CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记 文章目录 CTF题型 SSTI(1) Flask-SSTI-labs 通关 题记前记获取键值或下标的方式获取属性的方式 Level 1 no wafLevel 2 bl[\{\{]Level 3 no waf and blindLevel 4 bl[[, ]]获取键值或下标 Level 5 bl[\, "]Level 6 bl[_]Level …

高通 8255 基本通信(QUP)Android侧控制方法说明

一&#xff1a;整体说明 高通8255芯片中&#xff0c;SPI IIC UART核心统一由QUP V3 进行控制 QUP V3为可编程模块&#xff0c;可以将不同通道配置为SPI IIC UART通路&#xff0c;此部分配置在QNX侧 QUP 资源可以直接被QNX使用&#xff0c;Android侧可以通过两种方法使用QUP资源…

一次完整的 HTTP 请求所经历的步骤

1&#xff1a; DNS 解析(通过访问的域名找出其 IP 地址&#xff0c;递归搜索)。 2&#xff1a; HTTP 请求&#xff0c;当输入一个请求时&#xff0c;建立一个 Socket 连接发起 TCP的 3 次握手。如果是 HTTPS 请求&#xff0c;会略微有不同。 3&#xff1a; 客户端向服务器发…

B树B+树,字典树详解,哈夫曼树博弈树

目录 B树&#xff1a;B-Tree B树 字典树&#xff1a;Trie Tree 哈夫曼树 博弈树 B树&#xff1a;B-Tree 多路平衡搜索树 1.M阶B树&#xff0c;就是M叉&#xff08;M个指针&#xff09;。 2.每个节点内记录个数<M-1。 3.根节点记录个数>1。 4.其余节点内记录个数&…

JavaScript之继承

继承 父类与子类 子类是父类的一个子集 比如&#xff1a;人类和医生类&#xff0c;医生类是人类的子集&#xff1b;人类是父类&#xff0c;医生类是子集 父类与子类在特性&#xff08;属性和方法&#xff09;上有什么关系 方法&#xff1a;子类对象可以调用父类原型上的方…

Nadaraya-Watson核回归

目录 基本原理 ​编辑 核函数的选择 带宽的选择 特点 应用 与注意力机制的关系 参考内容 在统计学中&#xff0c;核回归是一种估计随机变量的条件期望的非参数技术。目标是找到一对随机变量 X 和 Y 之间的非线性关系。 在任何非参数回归中&#xff0c;变量 Y 相对于变量…

Jenkins 一个进程存在多个实例问题排查

Jenkins 一个进程存在多个实例问题排查 最近Jenkins升级到2.440.1​版本后&#xff0c;使用tomcat​服务部署&#xff0c;发现每次定时任务总会有3-4个请求到我的机器人上&#xff0c;导致出现奇奇怪怪的问题。 问题发现 机器人运行异常&#xff0c;总有好几个同时请求的服务。…

Selenium 自动化 —— 使用WebDriverManager自动下载驱动

上一篇文章 入门和 Hello World 实例 中&#xff0c;我们提供了一个最简单的 Selenium 上手的例子。 但是某一天&#xff0c;突然发现相同的代码居然运行报错了。这是怎么回事呢&#xff1f; 日志排查 日志中其实提示的很明显了&#xff1a;Chrome浏览器和Chrome WebDriver的…

python中如何解析Html

在最近需要的需求中&#xff0c;需要 python 获取网页内容&#xff0c;并从html中获取到想要的内容。这里记录一下两个比较常用的python库对html的解析。 1. BeautifulSoup 它是一个非常流行的python脚本库&#xff0c;用于解析HTML和XML文档。如果你对 java 很熟悉&#xff…

软件测试教程 自动化测试之Junit框架

文章目录 1. 什么是 Junit &#xff1f;2. 常见的注解2.1 Test2.2 BeforeAll&#xff0c;AfterAll2.3 BeforeEach&#xff0c;AfterEach 3. 测试用例顺序指定4. 参数化4.1 单个参数4.2 多个参数4.3 通过方法生成 5. 测试套件6. 断言6.1 断言相等6.2 断言不相等6.3 断言为空6.4 …