目录
8. 通过C语言调用Python代码,需要先安装libpython3的dev依赖库
b. 点击头像,创建AccessKey用于后续访问阿里云API的密钥
1. 首先将USB摄像头插入到Orange Pi开发板的USB接口中
2. 通过 lsmod | grep uvcvideo | grep -v grep ,在当前加载的内核模块列表中可看到包含 uvcvideo 的模块信息。
3. 通过V4l2-ctl 命令可以看到USB摄像头的设备节点信息
2.1 进入语音模块官网 http://www.smartpi.cn/#/
一. python环境搭建
1. 更新软件包
sudo apt update
2. 安装编译python需要的环境
sudo apt install -y build-essential zliblg-dev \
libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev \
libreadline-dev libffi-dev curl libbz2-dev
3. 从python官网下载源代码
wget https://www.python.org/ftp/python/3.10.x/Python-3.10.x.tgz
注意:替换3.10.x为你想要下载的具体的版本号
4. 解压源代码包
tar -xvf Python-3.10.x.tgz -C /usr/local/
注意:替换3.10.x为你想要下载的具体的版本号
5. 编译和安装
a.运行configure脚本来准备编译环境,且指定安装路径
./configure --prefix=/usr/local/python3.10
b.make命令进行编译
make -j4
c.最后使用make install命令来安装Python
sudo make install
d.看当前python版本
python3 --version
8. 通过C语言调用Python代码,需要先安装libpython3的dev依赖库
首先通过以下命令验证是否存在python3的dev包
dpkg -l | grep libpython3
若没有,则通过apt命令安装相关的dev包
sudo apt install libpython3.10-dev
二. C语言执行python语句
1. C语言调用Python语句例子
#include "Python.h"
int main(){
Py_Initialize(); //初始化
PyRun_SimpleString("print('funny')");
Py_Finalize(); //释放资源
}
gcc A.c -o A.out -I /usr/include/python3.10 -l python3.10 编译
./A.out 运行
2. C语言调用无参Python函数
#nopara.py文件
def say_funny():
print('funny')
#nopara.c文件
#include <Python.h>
int main(){
Py_Initialize(); //初始化
//导入sys模块
PyObject *sys = PyImport_ImportModule("sys");
//获取sys模块的path属性
PyObject *path = PyObject_GetAttrString(sys,"path");
//将当前路径添加到sys.path中
PyList_Append(path, PyUnicode_FromString("."));
//导入nopara模块
PyObject *pModule = PyImport_ImportModule("nopara");
//获取nopara模块的say_funny对象
PyObject *pFunc = PyObject_GetAttrString(pModule,"say_funny");
//调用say_funny函数并获取其返回值
PyObject *pValue = PyObject_CallObject(pFunc,NULL);
if(!pValue){
PyErr_Print();
printf("ERROR : function call failed\n");
return 1;
}
//释放所有的Python对象
Py_DECREF(pValue);
Py_DECREF(pFunc);
Py_DECREF(pModule);
//关闭Python解释器
Py_Finalize();
return 0;
}
编译 gcc nopara.c -o nopara -I /usr/include/python3.10/ -l python3.10
运行 ./nopara
3. C语言调用有参Python函数
para.py文件
def say_funny(category):
print(category)
return category
para.c文件
#include <Python.h>
int main(){
Py_Initialize(); //初始化
//导入sys模块
PyObject *sys = PyImport_ImportModule("sys");
//获取sys模块的path属性
PyObject *path = PyObject_GetAttrString(sys,"path");
//将当前路径添加到sys.path中
PyList_Append(path, PyUnicode_FromString("."));
//导入nopara模块
PyObject *pModule = PyImport_ImportModule("para");
//获取nopara模块的say_funny对象
PyObject *pFunc = PyObject_GetAttrString(pModule,"say_funny");
//创建一个字符串作为参数-----------------------------------------------
char *category = "comedy";
PyObject *pArgs = Py_BuildValue("(s)",category);
//调用say_funny函数并获取其返回值-----------------------------------
PyObject *pValue = PyObject_CallObject(pFunc,pArgs);
//返回值转换为C类型
char *result = NULL;
PyArg_Parse(pValue,"s",&result);
//打印返回值
printf("pValue=%s\n",result);
//释放所有的Python对象------------------------------------------------
Py_DECREF(pValue);
Py_DECREF(pFunc);
Py_DECREF(pModule);
//关闭Python解释器
Py_Finalize();
return 0;
}
编译 gcc para.c -o para -I /usr/include/python3.10/ -lpython3.10
运行 ./para
三. 智能垃圾桶项目
1.阿里云识别方案
对应官网地址如下:能力展示-阿里云视觉智能开放平台
a. 进入图像识别页面后,点击立即开通
b. 点击头像,创建AccessKey用于后续访问阿里云API的密钥
c. 开发接入步骤
a. 点击SDK总览
b. 选择Python语言
c. 复制图像识别的命令 pip install alibabacloud_imagerecog20190930
粘贴至Orangepi中,下载阿里云SDK工具包
d. 下载完成后,配置环境,<access_key_id>以及<access_key_secret>替换为刚才创
建的AccessKey(用双引号包含)
在 ~/.bashrc 和 /etc/profile 的末尾输入下面俩行,保存退出
export ALIBABA_CLOUD_ACCESS_KEY_ID=<access_key_id>
export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<access_key_secret>
e. 点击垃圾分类识别技术文档,点击垃圾分类识别示例代码,复制文件在本地或可访问
的URL的Python代码,把场景一的注释去掉,把场景二的代码加以注释。修改场景一
的文件路径为我要识别的图片的路径。
f. 运行 python3 test.db
2. C语言调用Python阿里云接口
garbage.py文件
# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_imagerecog20190930
import os
import io
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='imagerecog.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
def alibabacloud_garbage():
#场景一:文件在本地
img = open(r'/home/orangepi/Pictures/test1.jpg', 'rb')
#场景二:使用任意可访问的url
#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'
#img = io.BytesIO(urlopen(url).read())
classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()
classifying_rubbish_request.image_urlobject = img
runtime = RuntimeOptions()
try:
# 初始化Client
client = Client(config)
response = client.classifying_rubbish_advance(classifying_rubbish_request, runtime)
# 获取整体结果
print(response.body)
return response.body.to_map()['Data']['Elements'][0]['Category']
except Exception as error:
printf(type('获取失败'))
return '获取失败'
garbagetest.c文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>
void garbage_init(void){
Py_Initialize(); //初始化
//导入sys模块
PyObject *sys = PyImport_ImportModule("sys");
//获取sys模块的path属性
PyObject *path = PyObject_GetAttrString(sys,"path");
//将当前路径添加到sys.path中
PyList_Append(path, PyUnicode_FromString("."));
}
void garbage_final(void){
//关闭Python解释器
Py_Finalize();
}
char *garbage_category(char *category){
//导入garbage模块
PyObject *pModule = PyImport_ImportModule("garbage");
//获取garbage模块的alibabacloud_garbage()对象
PyObject *pFunc = PyObject_GetAttrString(pModule,"alibabacloud_garbage");
//调用alibabacloud_garbage()函数并获取其返回值
PyObject *pValue = PyObject_CallObject(pFunc,NULL);
char *result = NULL;
PyArg_Parse(pValue,"s",&result);
category = (char *)malloc(sizeof(char) * (strlen(result) + 1));
memset(category,0,(strlen(result) + 1));
strncpy(category,result,(strlen(result) + 1));
//释放所有的Python对象
Py_DECREF(pValue);
Py_DECREF(pFunc);
Py_DECREF(pModule);
return category;
}
int main(){
char *category = NULL;
garbage_init();
category = garbage_category(category);
printf("category=%s\n",category);
garbage_final();
free(category);
return 0;
}
编译 gcc -o garbagetest garbagetest.c -I /usr/include/python3.10/ -l python3.10
运行 ./garbagetest
四. 香橙派使用摄像头
操作如下:
1. 首先将USB摄像头插入到Orange Pi开发板的USB接口中
2. 通过 lsmod | grep uvcvideo | grep -v grep ,在当前加载的内核模块列表中可看到包含 uvcvideo 的模块信息。
3. 通过V4l2-ctl 命令可以看到USB摄像头的设备节点信息
a. sudo apt update
b. sudo apt install -y v4l-utils
c. v4l2-ctl --list-devices
4. 使用fswebcam测试USB摄像头
sudo apt-get install -y fswebcam
安装完后可通过以下命令拍照
sudo fswebcam -d /dev/video1 --no-banner -r 1280x720 -S 5 /home/orangepi/Pictures/image.jpg
-d选项用于指定USB摄像头的设备节点, --no-banner用于去除照片的水印, -r选项用于指定照片的分辨率, -S选项用设置于跳过前面的帧数,/home/orangepi/Pictures/image.jpg 用于设置生成的照片的名字和路径
5. 使用 mjpg-streamer测试USB摄像头
a. git clone https://gitee.com/leeboby/mjpg-streamer 下载mjpg-streamer项目的源代码
b. sudo apt-get install -y cmake libjpeg8-dev 下载对该软件进行编译的软件
c. cd mjpg-streamer-experimental/
d. make -j4 编译
e. sudo make install 安装
f. export LD_LIBRARY_PATH=.
g. sudo ./mjpg_streamer -i "./input_uvc.so -d /dev/video1 -u -f 30" -o "./output_http.so -w ./www" 启动mjpg-streamer
h. 浏览器中搜索 开发板的IP地址:8080 ,可以查看到抓拍到的照片
i. /mjpg-streamer/mjpg-streamer-experimental 目录下创建start.sh脚本
修改其里面的 ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"
变为 ./mjpg_streamer -i "./input_uvc.so -d /dev/video1 -u -f 30" -o "./output_http.so -w ./www"
j. ./start.sh 既可以运行摄像头了
五. 语言模块配置
1. 语言模块交互示意图
2. 语音模块配置
2.1 进入语音模块官网 http://www.smartpi.cn/#/
2.2 Pin脚配置
2.3 唤醒词自定义
2.4 命令词自定义
2.5 控制详情
2.6 下载固件
2.7 进行烧录
六 .代码
garbage.py
# -*- coding: utf-8 -*-
# 引入依赖包
# pip install alibabacloud_imagerecog20190930
import os
import io
from urllib.request import urlopen
from alibabacloud_imagerecog20190930.client import Client
from alibabacloud_imagerecog20190930.models import ClassifyingRubbishAdvanceRequest
from alibabacloud_tea_openapi.models import Config
from alibabacloud_tea_util.models import RuntimeOptions
config = Config(
# 创建AccessKey ID和AccessKey Secret,请参考https://help.aliyun.com/document_detail/175144.html。
# 如果您用的是RAM用户的AccessKey,还需要为RAM用户授予权限AliyunVIAPIFullAccess,请参考https://help.aliyun.com/document_detail/145025.html
# 从环境变量读取配置的AccessKey ID和AccessKey Secret。运行代码示例前必须先配置环境变量。
access_key_id=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_ID'),
access_key_secret=os.environ.get('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
# 访问的域名
endpoint='imagerecog.cn-shanghai.aliyuncs.com',
# 访问的域名对应的region
region_id='cn-shanghai'
)
def alibabacloud_garbage():
#场景一:文件在本地
img = open(r'/home/orangepi/Pictures/test3.jpg', 'rb')
#场景二:使用任意可访问的url
#url = 'https://viapi-test-bj.oss-cn-beijing.aliyuncs.com/viapi-3.0domepic/imagerecog/ClassifyingRubbish/ClassifyingRubbish1.jpg'
#img = io.BytesIO(urlopen(url).read())
classifying_rubbish_request = ClassifyingRubbishAdvanceRequest()
classifying_rubbish_request.image_urlobject = img
runtime = RuntimeOptions()
try:
# 初始化Client
client = Client(config)
response = client.classifying_rubbish_advance(classifying_rubbish_request, runtime)
# 获取整体结果
print(response.body)
return response.body.to_map()['Data']['Elements'][0]['Category']
except Exception as error:
printf(type('获取失败'))
return '获取失败'
garbage.h
//防止头文件的重复包含
#ifndef __GARBAGE__H
#define __GARBAGE__H
#include <Python.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void garbage_init(void);
void garbage_final(void);
char *garbage_category(char *category);
// 增加拍照指令和照片路径宏定义
#define WGET_CMD "wget http://192.168.144.202:8080/?action=snapshot -O /home/orangepi/Pictures/test3.jpg"
#define GARBAGE_FILE "/home/orangepi/Pictures/test3.jpg"
#endif
garbage.c
#include "garbage.h"
void garbage_init(void){
Py_Initialize(); //初始化
//导入sys模块0
PyObject *sys = PyImport_ImportModule("sys");
//获取sys模块的path属性
PyObject *path = PyObject_GetAttrString(sys,"path");
//将当前路径添加到sys.path中
PyList_Append(path, PyUnicode_FromString("."));
}
void garbage_final(void){
//关闭Python解释器
Py_Finalize();
}
char *garbage_category(char *category){
//导入garbage模块
PyObject *pModule = PyImport_ImportModule("garbage");
if ( !pModule ) {
PyErr_Print();
printf("Error:failed to load garbage.py\n");
goto FAILED_MODULE;
}
//获取garbage模块的alibabacloud_garbage()对象
PyObject *pFunc = PyObject_GetAttrString(pModule,"alibabacloud_garbage");
if ( !pFunc ) {
PyErr_Print();
printf("Error:failed to load alibabclound_garbage\n");
goto FAILED_FUNC;
}
//调用alibabacloud_garbage()函数并获取其返回值
PyObject *pValue = PyObject_CallObject(pFunc, NULL);
if ( !pValue ) {
PyErr_Print();
printf("Error:function call failed\n");
goto FAILED_VALUE;
}
char *result = NULL;
if ( !PyArg_Parse(pValue, "s", &result) ) {
PyErr_Print();
printf("Error parse failed");
goto FAILED_RESULT;
}
category = (char*)malloc(sizeof(char)*(strlen(result)+1));
memset(category,0,(strlen(result)+1));
strncpy(category,result,(strlen(result)+1));
FAILED_RESULT:
Py_DECREF(pValue);
FAILED_VALUE:
Py_DECREF(pFunc);
FAILED_FUNC:
Py_DECREF(pModule);
FAILED_MODULE:
return category;
}
myoled.h
#ifndef __MY_OLED__H
#define __MY_OLED__H
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "oled.h"
#include "font.h"
#define FILENAME "/dev/i2c-3"
static struct display_info disp;
int oled_show(void *arg);
int myoled_init(void);
#endif
myoled.c
#include "myoled.h"
int oled_show(void *arg) {
unsigned char *buffer = (unsigned char *)arg;
oled_putstrto(&disp, 0,9+1, "This garbage is:");
disp.font = font2;
switch(buffer[2]){
case 0x41:
oled_putstrto(&disp, 0,20, "dry waste");
break;
case 0x42:
oled_putstrto(&disp, 0,20, "wet waste");
break;
case 0x43:
oled_putstrto(&disp, 0,20, "recyclable waste");
break;
case 0x44:
oled_putstrto(&disp, 0,20, "hazardous waste");
break;
case 0x45:
oled_putstrto(&disp, 0,20, "recognition waste");
break;
}
disp.font = font2;
//将显示缓冲区内容发送到OLED显示器进行渲染
oled_send_buffer(&disp);
return 0;
}
int myoled_init(void){
int e;
disp.address = OLED_I2C_ADDR;
disp.font = font2;
e = oled_open(&disp, FILENAME);
e = oled_init(&disp);
return 0;
}
pwm.h
#ifndef __PWM__H
#define __PWM__H
#include <wiringPi.h>
#include <softPwm.h>
#define PWM_GARBAGE 7
#define PWM_RECOVERABLE_GARBAGE 5
void pwm_write(int pwm_pin);
void pwm_stop(int pwm_pin);
#endif
pwm.c
#include "pwm.h"
// 根据公式:PWMfreq = 1 x 10^6 / (100 x range),要得到 PWM 频率为 50Hz,则 range 为 200,即周期分为 200步,控制精度相比硬件 PWM 较低。
void pwm_write(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin, 0, 200); // range 设置周期分为 200步,周期20ms
softPwmWrite(pwm_pin, 15); // 1.5ms 90度
delay(1000);
softPwmStop(pwm_pin);
}
void pwm_stop(int pwm_pin)
{
pinMode(pwm_pin, OUTPUT);
softPwmCreate(pwm_pin, 0, 200); // range 设置周期分为 200步,周期20ms
softPwmWrite(pwm_pin, 5); // 0.5ms 0度
delay(1000);
softPwmStop(pwm_pin);
}
socket.h
#ifndef __SOCKET__H
#define __SOCKET__H
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
#define IPADDR "192.168.144.202" //IP地址
#define IPPORT "8192" //端口号
#define BUF_SIZE 6
int socket_init(const char *ipaddr, const char *port);
#endif
socket.c
#include "socket.h"
int socket_init(const char *ipaddr, const char *port) { //传参ipaddr为IP地址,port为端口号
int s_fd = -1;
int ret = -1;
struct sockaddr_in s_addr;
memset(&s_addr, 0, sizeof(struct sockaddr_in));
//1. 创建网络套接字------------------------------------------------------------------------
//int socket(int domain, int type, int protocol);
s_fd = socket(AF_INET, SOCK_STREAM, 0); //配置网络类型为为因特尔网
//配置数据传输的协议为TCP协议
//一般写0,根据前俩个配置的参数自动去选择合适的协议
if (s_fd == -1) {
//发生系统调用错误时,打印错误描述
perror("socket");
return -1;
}
//2.绑定本地IP地址和端口号到socket网络套接字上-------------------------------------------------
s_addr.sin_family = AF_INET;
//进行大小序转化
s_addr.sin_port = htons(atoi(port));
inet_aton(ipaddr, &s_addr.sin_addr);
//int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
//我们一般不用第二个结构体,用的是下面这个结构体
// struct sockaddr_in{
// uint16 sin_family; /*网络类型*/
// uint16 sin_port; /* Port number. */
// uint32 sin_addr.s_addr; /* Internet address. */
// unsigned char sin_zero[8]; /* Pad to size of `struct sockaddr'. */
// };
ret = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
if (ret == -1) {
//发生系统调用错误时,打印错误描述
perror("bind");
return -1;
}
//3.将socket套接字变为监听套接字,准备接受客户端的连接---------------------------------------------
//最多接受一个用户的连接
ret = listen(s_fd, 1);
if (ret == -1) {
//发生系统调用错误时,打印错误描述
perror("listen");
return -1;
}
return s_fd;
}
uartTool.h
#ifndef __UARTTOOL__H
#define __UARTTOOL__H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define SERIAL_DEV "/dev/ttyS5"
#define BAUD 115200
int myserialOpen (const char *device, const int baud);
void myserialClose (const int fd);
void serialSendstring (const int fd, const unsigned char *s,int len);
int serialGetstring (const int fd,char *buffer);
#endif
uartTool.c
#include "uartTool.h"
//打开串口并配置串口属性
int myserialOpen (const char *device, const int baud)
{
struct termios options ;
speed_t myBaud ;
int status, fd ;
switch (baud)
{
case 9600: myBaud = B9600 ; break ;
case 115200: myBaud = B115200 ; break ;
}
//=======================================1. 打开串口=======================================
fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK);
//将fd 的状态标记设置为可读写模式,确保文件描述符可以被读取和写入
fcntl (fd, F_SETFL, O_RDWR) ;
//=======================================2. 配置串口属性=======================================
//获取当前串口的属性配置,并将其保存在 options 结构体中
tcgetattr (fd, &options) ;
//设置串口为原始模式
cfmakeraw (&options) ;
cfsetispeed (&options, myBaud) ;
cfsetospeed (&options, myBaud) ;
//配置串口
options.c_cflag |= (CLOCAL | CREAD) ;
options.c_cflag &= ~PARENB ;
options.c_cflag &= ~CSTOPB ;
options.c_cflag &= ~CSIZE ;
options.c_cflag |= CS8 ;
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
options.c_oflag &= ~OPOST ;
options.c_cc [VMIN] = 0 ;
options.c_cc [VTIME] = 100 ;
//=======================================3. 将修改后的串口属性立即应用到串口=======================================
//将修改后的串口属性应用到串口
tcsetattr (fd, TCSANOW, &options) ;
//=======================================4. 设置串口为已经准备好发送和接受数据=======================================
//获取当前串口控制信号的状态,并将状态值保存在变量 status 中
ioctl (fd, TIOCMGET, &status);
//控制信号设置为高电平状态,表示串口已经准备好发送和接收数据
status |= TIOCM_DTR ;
status |= TIOCM_RTS ;
//将修改后的控制信号状态应用到串口,确保设备正确配置和准备好进行数据的发送和接收操作。
ioctl (fd, TIOCMSET, &status);
//=======================================5. 延时一段时间确保串口配置成功=======================================
usleep (10000) ;// 10mS
return fd ;
}
//关闭串口
void myserialClose (const int fd)
{
close (fd) ;
}
//发送字符串
void serialSendstring (const int fd, const unsigned char *s,int len)
{
int ret;
ret = write (fd, s, len);
if(ret < 0){
printf("Serial Puts Error\n");
}
}
//接受字符串
int serialGetstring (const int fd,char *buffer)
{
int n_read;
n_read = read (fd, buffer, 32);
return n_read;
}
garbagetest.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Python.h>
#include <wiringPi.h>
#include <unistd.h>
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
#include "socket.h"
int serial_fd = -1;
pthread_cond_t cond;
pthread_mutex_t mutex;
/*判断摄像头是否正在运行*/
static int detect_process(const char *process_name)
{
int n = -1;
FILE *strm;
char buf[128] = {0};
//将该查询进程命令写入到buf中
sprintf(buf, "ps -ax | grep %s | grep -v grep", process_name);
//popen函数用于创建一个管道,其内部调用fork创建一个子进程,在该子进程调用execl()函数执行查询命令
if ((strm = popen(buf, "r")) != NULL){
if (fgets(buf, sizeof(buf), strm) != NULL){
n = atoi(buf);
}
}else{ //若检测到摄像头没有成功启用,则返回-1
return -1;
}
pclose(strm);
return n;
}
void *pget_voice(void *arg){
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};
int len = 0;
if (serial_fd == -1)
{
printf("%s|%s|%d : open serial failed\n",__FILE__,__func__,__LINE__);
pthread_exit(0);
}
while(1){
len = serialGetstring(serial_fd, buffer);
printf("%s|%s|%d,len=%d, buf[2]=0x%x\n",__FILE__,__func__,__LINE__,len, buffer[2]);
//若串口中内容代表的是识别垃圾口令
if (len > 0 && buffer[2] == 0x46 && buffer[3] == 0x00)
{
pthread_mutex_lock(&mutex);
buffer[2] = 0x00;
//发送信号唤醒图像识别线程
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
pthread_exit(0);
}
void *psend_voice(void *arg){
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
if(-1 == serial_fd){
printf("%s|%s|%d : open serial failed\n",__FILE__,__func__,__LINE__);
pthread_exit(0);
}
if(NULL != buffer){
serialSendstring(serial_fd,buffer,6);
}
pthread_exit(0);
}
void *popen_trash_can(void *arg){
pthread_detach(pthread_self());
unsigned char *buffer = (unsigned char *)arg;
if (buffer[2] == 0x41 || buffer[2] == 0x42)
{
printf("%s|%s|%d : buffer[2]=0x%x\n",__FILE__,__func__,__LINE__,buffer[2]);
pwm_write(PWM_RECOVERABLE_GARBAGE);
delay(2000);
pwm_stop(PWM_RECOVERABLE_GARBAGE);
}
else if (buffer[2] != 0x45)
{
printf("%s|%s|%d : buffer[2]=0x%x\n",__FILE__,__func__,__LINE__,buffer[2]);
printf("start\n");
pwm_write(PWM_GARBAGE);
delay(2000);
pwm_stop(PWM_GARBAGE);
}
pthread_exit(0);
}
void *poled_show(void *arg){
unsigned char *buffer = (unsigned char *)arg;
pthread_detach(pthread_self());
myoled_init();
oled_show(arg);
pthread_exit(0);
}
void *pcategory(void *arg){
unsigned char buffer[6] = {0xAA, 0x55, 0x00, 0x00, 0x55, 0xAA};
char *category = NULL;
pthread_t send_voice_tid, trash_tid,oled_show_tid;
printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
while(1){
printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond,&mutex);
pthread_mutex_unlock(&mutex);
printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
buffer[2] = 0x00;
system(WGET_CMD);
if(0 == access(GARBAGE_FILE,F_OK)){
category = garbage_category(category);
if (strstr(category, "干垃圾"))
{
buffer[2] = 0x41;
}
else if (strstr(category, "湿垃圾"))
{
buffer[2] = 0x42;
}
else if (strstr(category, "可回收垃圾"))
{
buffer[2] = 0x43;
}
else if (strstr(category, "有害垃圾"))
{
buffer[2] = 0x44;
}
else
{
buffer[2] = 0x45;
}
}else{
buffer[2] = 0x45;
}
//语音播报
pthread_create(&trash_tid,NULL,psend_voice,(void *)buffer);
//垃圾桶开盖
pthread_create(&send_voice_tid,NULL,popen_trash_can,(void *)buffer);
//oled显示垃圾类型
pthread_create(&oled_show_tid,NULL,poled_show,(void *)buffer);
//buffer[2] = 0x00; //传递了buffer的地址给子线程,在主线程中修改了buffer[2]的值后,子线程中的buffer的值也会被改变
}
pthread_exit(0);
}
void *pget_socket(void *arg) {
int s_fd = -1;
int c_fd = -1;
char buffer[6];
int nread = -1;
struct sockaddr_in c_addr;
memset(&c_addr, 0, sizeof(struct sockaddr_in));
//a.创建网络套接字 b.绑定IP地址和端口号 c.将socket套接字变为监听套接字准备接受客户端的连接
s_fd = socket_init(IPADDR, IPPORT);
printf("%s|%s|%d: s_fd=%d\n", __FILE__, __func__, __LINE__, s_fd);
if (s_fd == -1) {
pthread_exit(0);
}
sleep(3);
int clen = sizeof(struct sockaddr_in);
while (1) {
//d. 服务器阻塞等待客户端的连接
c_fd = accept(s_fd, (struct sockaddr *)&c_addr, &clen);
int keepalive = 1; //开启keepalive功能
int keepidle = 5; //在 keepalive 开启的情况下,在该时间内都没有数据的传输,则会发送探测包
int keepcnt = 3; //表示探测尝试的次数,当尝试次数达到指定值仍未得到回复,--------------连接将被关闭。---------------
int keepintvl = 3; //如果一次探测包发出后,在等待回复的时间内未收到回复,会再次发送探测包,间隔为该值。
setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive, sizeof(keepalive));
setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
//输出接受到连接后客户端的信息,包括源IP地址和端口号,大小端字节序转化
printf("%s|%s|%d: Accept a connection from %s:%d\n", __FILE__, __func__, __LINE__,inet_ntoa(c_addr.sin_addr), ntohs(c_addr.sin_port));
//与客户端未成功连接
if (c_fd == -1) {
perror("accept");
continue;
}
while (1) {
memset(buffer, 0, sizeof(buffer));
nread = recv(c_fd, buffer, sizeof(buffer), 0);
printf("%s|%s|%d: nread=%d, buffer=%s\n", __FILE__, __func__, __LINE__, nread, buffer);
if (nread > 0) {
if (strstr(buffer, "open")) {
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
//nread == 0的情况:1.与客户端未成功连接 2.连接上了,但是keepalive功能检测到长时间未交互自动关闭了连接
//nread == -1的情况:接受数据时发生了未知的错误
} else if (nread == 0 || nread == -1) {
break;
}
}
//nread == -1的情况,退出循环后,关闭与客户端的连接,
close(c_fd);
}
pthread_exit(0);
}
int main(int argc, char *argv[])
{
int ret = -1;
int len = 0;
pthread_t get_voice_tid, category_tid, get_socket_tid;
wiringPiSetup(); //初始化wiringPi库
garbage_init();
/*1. 判断摄像头是否已经启动*/
ret = detect_process("mjpg_streamer"); // 用于判断 mjpg_streamer 服务是否已经启动
if (ret == -1)
{
printf("%s|%s|%d : open mjpg_streamer failed\n",__FILE__,__func__,__LINE__);
goto END;
}
/*2. 打开串口*/
serial_fd = myserialOpen(SERIAL_DEV, BAUD);
if (serial_fd == -1)
{
goto END;
}
//开语音线程
printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
pthread_create(&get_voice_tid,NULL,pget_voice,NULL);
//开网络线程
pthread_create(&get_socket_tid,NULL,pget_socket,NULL);
//开阿里云交互线程
printf("%s|%s|%d\n",__FILE__,__func__,__LINE__);
pthread_create(&category_tid,NULL,pcategory,NULL);
pthread_join(get_voice_tid,NULL);
pthread_join(category_tid,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
close(serial_fd);
END:
garbage_final();
return 0;
}
编译
gcc -o garbagetest garbagetest.c garbage.c myoled.c uartTool.c pwm.c socket.c -I /usr/include/python3.10/ -lpython3.10 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt
运行
sudo -E ./garbagetest