基于Linux的智能垃圾桶项目

目录

一. python环境搭建

1. 更新软件包

2. 安装编译python需要的环境

3. 下载python源码

4. 解压源码包

5. 配置

6. 编译

7. 安装

8. 通过C语言调用Python代码,需要先安装libpython3的dev依赖库

二. C语言执行python语句

1. C语言调用Python语句例子

2. C语言调用无参Python函数

#nopara.py文件

#nopara.c文件

3. C语言调用有参Python函数

para.py文件

para.c文件

三. 智能垃圾桶项目

1.阿里云识别方案

对应官网地址如下:能力展示-阿里云视觉智能开放平台

a. 进入图像识别页面后,点击立即开通

b. 点击头像,创建AccessKey用于后续访问阿里云API的密钥

c. 开发接入步骤

2. C语言调用Python阿里云接口  

四. 香橙派使用摄像头

1. 首先将USB摄像头插入到Orange Pi开发板的USB接口中

2. 通过  lsmod | grep uvcvideo | grep -v grep ,在当前加载的内核模块列表中可看到包含 uvcvideo 的模块信息。

3. 通过V4l2-ctl 命令可以看到USB摄像头的设备节点信息

4. 使用fswebcam测试USB摄像头

5. 使用 mjpg-streamer测试USB摄像头 

五. 语言模块配置

1. 语言模块交互示意图

2. 语音模块配置

2.1 进入语音模块官网 http://www.smartpi.cn/#/

2.2 Pin脚配置

2.3 唤醒词自定义 

 2.4 命令词自定义

2.5 控制详情

​编辑

2.6 下载固件

 2.7 进行烧录

六 .代码

garbage.py

garbage.h

garbage.c

myoled.h

myoled.c

pwm.h

pwm.c

 socket.h

socket.c

uartTool.h

uartTool.c

编译

运行


一. 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@尘音

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值