【全志H616】【开源】 ARM-Linux 智能分拣项目:阿里云、网络编程、图像识别

news2024/11/15 11:56:21

【全志H616】【开源】 ARM-Linux 智能分拣项目:阿里云、网络编程、图像识

文章目录

  • 【全志H616】【开源】 ARM-Linux 智能分拣项目:阿里云、网络编程、图像识
    • 1、实现功能
    • 2、软件及所需环境
    • 3、逻辑流程图及简述
      • 3.1 完整逻辑流程图
      • 3.2 硬件接线
      • 3.3 功能简述
        • 3.3.1 main()
        • 3.3.2 main函数中打开的语音、网络、阿里云交互线程
          • 3.3.2.1 **语音线程**
          • 3.3.2.2 网络线程
          • 3.3.2.3 阿里云交互线程
          • 3.3.2.4 阿里云交互线程`pcategory`开的3个线程
    • 4、实验程序
      • 4.1 `uartTool` 串口
      • 4.2 `socket` 网络
      • 4.3 阿里云 `garbage` 识别
      • 4.4 `PWM` sg90舵机
      • 4.5 OLED` 显示
      • 4.6 `main.c`
    • 5、实验流程和执行结果
      • 5.1 配置客户端和`mjpg_Stream`
      • 5.2 调试和运行
      • 5.3 开始识别
    • 6、总结

1、实现功能

  • 语音接入控制垃圾分类识别并触发垃圾桶的开关盖,并根据垃圾类型开关不同类型垃圾桶
  • 利用TCPSocket编程,实现Sockect发送指令远程控制垃圾分类识别,引入心跳包来检测客户端状态,防止客户端异常关闭导致进程堵塞
  • 为什么要用 TCP的socket编程 通过网络来控制呢?
  • 是为了可以通过远程PC来实现对硬件的控制
  • 调用阿里云接口实现 物品的动态识别服务,将识别到的物品类型返回,并最终通过语音模块播报
  • 语音播报垃圾物品类型
  • OLED显示垃圾物品类型
  • 多线程编程 使用多线程编程来解决多并发的问题

看下面的流程图会更清晰

2、软件及所需环境

Ubantu-22.04MoberXtermarach64-linux-gnuVCS-ssh、全志开发板、SU-03T语音模块、OLED显示屏、USB摄像头、SG90舵机

语音烧入软件&SU-03T语音模块的配置文件:

链接: https://pan.baidu.com/s/1SfNVrNmBR02YqE7lU2LiVQ?pwd=2222 提取码: 2222 复制这段内容后打开百度网盘手机App,操作更方便哦

网络调试助手

链接: https://pan.baidu.com/s/1HkVjyYVYZ9NdhQNwx-qTGQ?pwd=2222 提取码: 2222 复制这段内容后打开百度网盘手机App,操作更方便哦

3、逻辑流程图及简述

3.1 完整逻辑流程图

在这里插入图片描述

3.2 硬件接线

  • 命令gpio readall 显示针脚的状态和配置信息。

在这里插入图片描述

3.3 功能简述

3.3.1 main()

我们先通过main函数来了解一下整个程序的概况

  • 定义线程在被创造 pthread_create后 ,需要的tid 参数;

    是一个指向 pthread_t 类型变量的指针,用于存储新创建线程的标识符。

  • 先进行初始化,wiringPiSetup() & garbage_init();

    • 对于witingPi库和阿里云接口初始化
  • 判断mjpg_streamer这个进程是否存在;detect_process("mjpg_streamer"); 这个函数是我们自己写的,在后文会提供;

    • mjpg_streamer 是一款开源的流媒体服务器软件,它能够从一系列输入插件(如USB摄像头)捕获视频数据,并通过网络以MJPG(Motion JPEG)格式实时传输
  • 打开串行接口 serial_fd = myserialOpen(SERIAL_DEV,BAUD);//可代替设备节点 & 波特率 返回值fd;判断是否打开成功

  • 打开语音、网络、阿里云交互线程 pthread_create()

  • 等待指定的线程结束其执行 pthread_join()

  • 销毁互斥锁(mutex)和条件变量(cond) pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond);

  • 关闭串口 close(serial_fd);

//main()
int main(int argc, char *argv[])
{
    printf("adsfasdf\n");
    int len = 0;
    int ret = -1;//标识
    char *category = NULL;
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    pthread_t get_voice_tid, category_tid,get_socket_tid;

    //初始化
    wiringPiSetup();//初始化wiringPi库
    garbage_init(); //初始化阿里云接口
    
    // 用于检测特定进程是否在运行。
    // 它通过调用系统命令ps -ax | grep process_name|grep -v grep来查找包含指定进程名的进程,并返回相应的进程号  
    // mjpg_streamer 是一款开源的流媒体服务器软件,它能够从一系列输入插件(如USB摄像头)捕获视频数据,并通过网络以MJPG(Motion JPEG)格式实时传输
    ret = detect_process("mjpg_streamer");
    if(-1 == ret)
    {
        printf("detect process failed\n");
        goto END;
    }

    //打开设备
    serial_fd = myserialOpen(SERIAL_DEV,BAUD);//可代替设备节点 & 波特率   返回值fd
    if(serial_fd == -1)//是否打开成功
    {
        goto END;    
    }//*pget_voice(void *arg)里面判断
    

    //printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);
    //开语音的线程
    pthread_create(&get_voice_tid,NULL,pget_voice,NULL);
    //开网络线程
    pthread_create(&get_socket_tid,NULL,pget_socket,NULL);
    //开阿里云交互线程
    pthread_create(&category_tid,NULL,pcategory,NULL);
        //get_voice_tid获取语音模块的数据
        //pthread_join用于等待指定的线程结束其执行
    pthread_join(get_voice_tid,NULL);
    pthread_join(category_tid,NULL);
    pthread_join(get_socket_tid,NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    close(serial_fd);
END:
    garbage_final; //释放所有引用的Python对象
    return 0;
}
3.3.2 main函数中打开的语音、网络、阿里云交互线程
3.3.2.1 语音线程

在main()函数中,用 pthread_create(&get_voice_tid,NULL,pget_voice,NULL);来开启语音线程

  • 判断该串口是否已经打开,用输入文件|函数|行号的形式来当调试信息

  • 开一个 while(1) 循环

    • 利用串口接收函数serialGetstring(serial_fd,buffer); //P8 & P10 ==> TXD.5 & RXD.5 //获取(读)传过来的数据

      这个函数来自 uartTool.c & uartTool.h 文件

    • 读取的数据buffer[2] 是否是 我先要的;

      0x46命令表示:语音播报了“识别垃圾类型” 可以在语音模块的配置文件里面修改;

      • 得到的信号是我想要的话,便执行 上锁pthread_mutex_lock(&mutex)–buffer[2]复位–发信号(利用条件变量) pthread_cond_signal(&cond)–关锁pthread_mutex_unlock(&mutex)

        互斥锁(mutex)和条件变量(cond)是多线程编程中常用的同步机制,用于控制对共享资源的访问和线程间的通信。

  • 进程退出pthread_exit(0);

  • main.c里面的函数

//获得 语音线程 pget_voice()
void *pget_voice(void *arg)
{
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    int len = 0;
    if(serial_fd == -1)//是否打开成功
    { 
        printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);//文件名-函数名-行号
        pthread_exit(0);
    }
    printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
    while(1) 
    {
        //获取(读)传过来的数据
        len = serialGetstring(serial_fd,buffer); //P8 & P10 ==> TXD.5 & RXD.5
        printf("%s|%s|%d, len=%d\n", __FILE__, __func__, __LINE__,len);
        if(len>0 && buffer[2]==0x46) //0x46命令:识别垃圾类型
        {
            printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
            
            //利用 互斥锁(mutex)和条件变量(condition variable)
            //互斥锁(mutex)和条件变量(cond)是多线程编程中常用的同步机制,用于控制对共享资源的访问和线程间的通信。
            //上锁
            pthread_mutex_lock(&mutex);
            buffer[2] = 0x00;
            //给阿里云发信号
            pthread_cond_signal(&cond);//发信号
            pthread_mutex_unlock(&mutex);
        } 
    } 
    pthread_exit(0);
}
  • uartTool.h
//uartTool.h
#ifndef __UARTTOOL_H
#define __UARTTOOL_H
int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, unsigned const char *s, int len); //写数据
int serialGetstring (const int fd, unsigned char *buffer); //读数据

#define SERIAL_DEV "/dev/ttyS5" // 可代替设备节点
#define BAUD 115200             // 代替波特率

#endif
//防止头文件中的声明反复区声明
  • uartTool.c
//uartTool.c 中的一个函数
int serialGetstring (const int fd, unsigned char *buffer)
{
	int n_read;
    //read() 用于从文件描述符(file descriptor)读取数据的 POSIX 标准函数之一
	n_read = read(fd,buffer,6);
    //printf("fd = %d\n",fd); 
    printf("BUFFER = %x,%x,%x\n",buffer[0],buffer[1],buffer[2]); 
	return n_read;
}
3.3.2.2 网络线程

main函数中 利用 pthread_create(&get_socket_tid,NULL,pget_socket,NULL);来开启socket网络线程

  • 初始化socket,这个就不详细讲解了,在前面的文章,socket网络编程有详细讲解

    Linux系统编程8–网络编程-CSDN博客

  • 在网络编程文件的基础上,这边还添加了 心跳包功能,

    • Socket客户端得断开有两种情况:

      • 1.客户端能够发送状态给服务器;正常断开,强制关闭客户端等,客户端能够做出反应。
      • 2.客户端不能发送状态给服务器;突然断网,断电,客户端卡死等,客户端根本没机会做出反应, 服务器更不了解客户端状态,导致服务器异常等待。
    • 因此,为了解决上述问题,引入TCP心跳包机制: 心跳包的实现,心跳包就是服务器定时向客户端发送查询信息,如果客户端有回应就代表连接正常, 类似于linux系统的看门狗机制。心跳包的机制有一种方法就是采用TCP_KEEPALIVE机制,它是一种用于 检测TCP连接是否存活的机制,它的原理是在一定时间内没有数据往来时,发送探测包给对方,如果对方 没有响应,就认为连接已经断开。TCP_KEEPALIVE机制可以通过设置一些参数来调整,如探测时间间 隔、探测次数等。

    • 查看当前系统的TCP KeepAlive参数

      sysctl net.ipv4.tcp_keepalive_time
      sysctl net.ipv4.tcp_keepalive_probes
      sysctl net.ipv4.tcp_keepalive_intvl
      

在这里插入图片描述

默认等待时间为7200s,查询9次,每次间隔75s

我们把它修改了,见 main.c 文件里的 void *pget_socket(void *fd) 函数

  • while(1)循环

    • 引入心跳包

    • 连接成功后,输出调试信息

    • while(1)

      • 读数据,如果读取成功,判断读取的数值是否是 “open”
        • 读取到"open" 便执行 上锁、给阿里云发信号、关锁的操作(和前文语音模块一样)
      • 没有读取到,或者读取错误,就break;
  • 关闭s_fd (ivp4 TCP)

  • 进程退出 pthread_exit(0);

  • main.c中的pget_socket函数

void *pget_socket(void *fd)
{
    int s_fd = -1;
    int c_fd = -1;
    char buffer[6];
    int nread = -1;
    struct sockaddr_in c_addr;//用于accept

    memset(&c_addr,0,sizeof(struct sockaddr_in));//清零
    s_fd = socket_init(IPADDR,IPPORT);
    printf("%s|%s|%d: s_fd=%d\n",__FILE__,__func__,__LINE__,s_fd);
    if(-1 == s_fd)
    {
        pthread_exit(0);
    }
    sleep(3);
    int clen = sizeof(struct sockaddr_in);

    while(1)
	{
        //accep第三个参数要求是指针//不连接,便堵塞,直到与客户端连接
		c_fd = accept(s_fd, (struct sockaddr *)&c_addr,&clen);
        // 心包跳
        int keepalive = 1; // 开启TCP KeepAlive功能
        int keepidle = 5; // tcp_keepalive_time 3s内没收到数据开始发送心跳包
        int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
        int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包
        setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,
        sizeof(keepalive));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof
        (keepidle));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof
        (keepcnt));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof
        (keepintvl));
		//
		
        //连接成功后,输出调试信息
        printf("--------ok?--------\n");
        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));//最后两个window主机的ip和端口号,可以在网络调试助手设置查看,
        printf("-----------ok!-----\n");
        if(c_fd == -1)
		{
			perror("accept");//终端打出错误:accept:---
            continue;
		}

        while(1)
        {
            memset(buffer,0,sizeof(buffer));
            nread = recv(c_fd,buffer,sizeof(buffer),0);// == n_read = read(c_fd,readBuf,sizeof(buffer));
            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);
                }
            }
            else if(0==nread || -1==nread)
            {
                break;
            }
        }
        close(c_fd);
    }
    pthread_exit(0);
}
  • socket.h socket初始化
//socket.h
#ifndef __SOCKET_H
#define __SOCKET_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //memset包含头文件
#include <sys/socket.h>
#include <sys/types.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h> //该头文件会与 #include <linux/in.h> 冲突
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
int socket_init(const char *ipaddr,const char *ipport);
#endif
  • socket.c socket的初始化
#include "socket.h"
int socket_init(const char *ipaddr,const char *ipport)
{
    int s_fd;
    int ret = -1;
	//sockaddr_in 是一个结构体,包含本机子ip地址和端口号等信息
	struct sockaddr_in s_addr;//用于bind
    memset(&s_addr,0,sizeof(struct sockaddr_in));//进行清空 


	//1、socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);  //ivp4,TCP
	if(-1 == s_fd)
	{
		perror("socket");//将上一个函数错误的原因输出
		return -1;
	}

	s_addr.sin_family = AF_INET;//ivp4
	s_addr.sin_port = htons(atoi(ipport));//htons() 返回网络字节的端口号 //ipport是第二个参数,端口号
	inet_aton(ipaddr,&s_addr.sin_addr);//字符串类型形式的“192.0.0.1”转换为网络能识别的格式//字符串-指针

	//2、bind
	ret = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
    if(-1 == ret)
	{
		perror("bind");//将上一个函数错误的原因输出
		return -1;
	}
	//3、listen
	ret = listen(s_fd,1);//同时允许一个监听
    if(-1 == ret)
	{
		perror("listen");//将上一个函数错误的原因输出
		return -1;
	}
    return s_fd;
}
3.3.2.3 阿里云交互线程

main()函数中,利用 pthread_create(&category_tid,NULL,pcategory,NULL); 来开阿里云交互线程

  • 定义线程在被创造 pthread_create后 ,需要的tid 参数;

    是一个指向 pthread_t 类型变量的指针,用于存储新创建线程的标识符。

  • while(1)循环

    • 线程上锁、等待信号 //cond 会合、关锁
    • buffer[2]复位
    • 拍照指令 system(WGET_CMD) 宏定义WGET_CMD:拍照命令,garbage.h 文件中
    • 识别垃圾图片是否存在,也就是是否拍摄成功access(GARBAGE_FILE, F_OK) GARBAGE_FILE 文件位置宏定义
    • 调用阿里云接口判断垃圾类型 category = garbage_category(category); 函数见 garbage.c & garbage.py
    • 判断 对应的垃圾类型,并对buffer[2] 赋不同的值
    • 再开三个线程
      • 开垃圾桶开盖线程:pthread_create(&trash_tid,NULL,psend_voice,(void*)buffer);
      • 开语音播报线程: pthread_create(&send_void_tid,NULL,popen_trash_can,(void*)buffer);
      • oled显示线程 :pthread_create(&oled_tid,NULL,poled_show,(void*)buffer);
    • 把识别到的垃圾的图片清除掉 remove(GARBAGE_FILE); GARBAGE_FILE 文件文章宏定义,见garbage.h
  • 退出进程 pthread_exit(0);

void *pcategory(void *arg)
{
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    char *category = NULL;
    pthread_t send_void_tid,trash_tid,oled_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;
        //define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg" //抓拍命令
        system(WGET_CMD);//宏定义:拍照命令,garbage.h 文件中
        // access函数是一个用于测试文件访问权限的函数,它通常用于检查某个文件是否可以被当前用户读取、写入或执行。
        // 包括F_OK(检查文件是否存在)、R_OK(检查读权限)、W_OK(检查写权限)和X_OK(检查执行权限)
        // 该函数返回值为0表示有权限,-1表示无权限或发生错误。
        if(0 == access(GARBAGE_FILE, F_OK))//GARBAGE_FILE 宏定义 识别到的垃圾图片是否存在
        {
            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_void_tid,NULL,popen_trash_can,(void*)buffer);
        //开oled显示线程
        pthread_create(&oled_tid,NULL,poled_show,(void*)buffer);

        // buffer[2]=0x00;//将buffer[2]清零
        remove(GARBAGE_FILE);//garbage.h 的宏定义 把识别到的垃圾的图片清除掉
    }
    pthread_exit(0);
}
3.3.2.4 阿里云交互线程pcategory开的3个线程
void *psend_voice(void *arg) //写数据,回传给语音模块
{   
    pthread_detach(pthread_self());
    unsigned char *buffer = (unsigned char *)arg;
    if(serial_fd == -1)//是否打开成功
    { 
        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;
    //这意味着线程结束时会自动释放其资源,
    //而不需要其他线程调用 pthread_join() 来等待其结束并回收资源
   
    if(buffer[2] == 0x41)
    {
        pwm_write(PWM_RECOVERABLE_GARBAGE); //对应wPi 5口,接着一个舵机
        printf("---------%x---------\n",buffer[2]);
        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);  //对应wPi 7口,接着另一个舵机
        delay(2000);
        pwm_stop(PWM_GARBAGE);
    }
    pthread_exit(0);
}

void *poled_show(void *arg)//oled显示当前识别的垃圾类型
{
    pthread_detach(pthread_self()); //这意味着线程结束时会自动释放其资源,
    myoled_init();
    oled_show(arg); //buffer

    pthread_exit(0);
}

4、实验程序

4.1 uartTool 串口

  • 串口的打开;发送、接收串口信息
//uartTool.h
#ifndef __UARTTOOL_H
#define __UARTTOOL_H
int myserialOpen (const char *device, const int baud);
void serialSendstring (const int fd, unsigned const char *s, int len); //写数据
int serialGetstring (const int fd, unsigned char *buffer); //读数据

#define SERIAL_DEV "/dev/ttyS5" // 可代替设备节点
#define BAUD 115200             // 代替波特率

#endif
//防止头文件中的声明反复区声明
//uartTool.c
#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>
#include "uartTool.h"//先到当前文件夹查看
#include "wiringSerial.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 ;
  	default:
  }
  
  if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
  return -1 ;
  
  fcntl (fd, F_SETFL, O_RDWR) ;

// Get and modify current options:

  tcgetattr (fd, &options) ;//get

  //标准化的操作 一般都是固定的
  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 ;	// Ten seconds (100 deciseconds)
  //标准化的操作
  tcsetattr (fd, TCSANOW, &options) ;//写入内核


  ioctl (fd, TIOCMGET, &status);//获得整型控制字TIOCMGET get出来
  status |= TIOCM_DTR ;
  status |= TIOCM_RTS ;//控制字加上标志位
  ioctl (fd, TIOCMSET, &status);//写入内核

  usleep (10000) ;	// 10mS
  return fd ;
}

//0XAA 0X55 0X46 0X00 0X55 0XAA
void serialSendstring (const int fd, const unsigned char *s, int len)//去写数据
{
	int ret;
  ret = write (fd, s, len);
  if (ret < 0)
 		printf("Serial Putchar Error\n");
}

int serialGetstring (const int fd, unsigned char *buffer)
{
	int n_read;
  //read() 用于从文件描述符(file descriptor)读取数据的 POSIX 标准函数之一
	n_read = read(fd,buffer,6);
  //printf("fd = %d\n",fd); 
  printf("BUFFER = %x,%x,%x\n",buffer[0],buffer[1],buffer[2]); 
	return n_read;
}

4.2 socket 网络

  • socket网络线程 + 心跳包
//socket.h
#ifndef __SOCKET_H
#define __SOCKET_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //memset包含头文件
#include <sys/socket.h>
#include <sys/types.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h> //该头文件会与 #include <linux/in.h> 冲突
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <errno.h>
int socket_init(const char *ipaddr,const char *ipport);
#endif
//socket.c
#include "socket.h"
int socket_init(const char *ipaddr,const char *ipport)
{
    int s_fd;
    int ret = -1;
	//sockaddr_in 是一个结构体,包含本机子ip地址和端口号等信息
	struct sockaddr_in s_addr;//用于bind
    memset(&s_addr,0,sizeof(struct sockaddr_in));//进行清空 

	//1、socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);  //ivp4,TCP
	if(-1 == s_fd)
	{
		perror("socket");//将上一个函数错误的原因输出
		return -1;
	}

	s_addr.sin_family = AF_INET;//ivp4
	s_addr.sin_port = htons(atoi(ipport));//htons() 返回网络字节的端口号 //ipport是第二个参数,端口号
	inet_aton(ipaddr,&s_addr.sin_addr);//字符串类型形式的“192.0.0.1”转换为网络能识别的格式//字符串-指针

	//2、bind
	ret = bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
    if(-1 == ret)
	{
		perror("bind");//将上一个函数错误的原因输出
		return -1;
	}
	//3、listen
	ret = listen(s_fd,1);//同时允许一个监听
    if(-1 == ret)
	{
		perror("listen");//将上一个函数错误的原因输出
		return -1;
	}

    return s_fd;
}

4.3 阿里云 garbage 识别

# 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 alibaba_garbage(): # 封装成一个函数,方便其他文件调用
  #场景一:文件在本地
  # img = open(r'/home/orangepi/garbage/lj/test2.jpg','rb')
  img = open(r'/tmp/garbage.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) # 默认
    # print(response.body.to_map()) # 打印出body的类型
    print(response.body.to_map()['Data']['Elements'][0]['Category']) # 打印出body的类型
    return response.body.to_map()['Data']['Elements'][0]['Category']
    # 提供调用方便
  except Exception as error:
    # 获取整体报错信息
    # print(error)
    # 获取单个字段
    # print(error.code)
    print("shibai")
    return '获取失败'

if __name__== "__main__":
  alibaba_garbage()
// garbage.h
#ifndef __GARBAGE__H
#define __GARBAGE__H
void garbage_init(void);//第一步做初始化,初始python的编译器包括导入当前路劲到sys.path里面
void garbage_final(void);
char *garbage_category(char *category);

//127.0.0.1
//通常被称为"本地回环地址"或"localhost";
//这个地址经常用于测试网络应用程序和诊断网络问题
//因为它确保数据能够在同一台计算机上进行循环传输,而无需真正通过网络传输。

#define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg" //抓拍命令
#define GARBAGE_FILE "/tmp/garbage.jpg"

#endif
//garbage.c
// C语言引用Python文件
#if 0
1、包含Python.h头文件,以便使用Python API。
2、使用void Py_Initialize()初始化Python解释器,
3、使用PyObject *PyImport_ImportModule(const char *name)和PyObject
*PyObject_GetAttrString(PyObject *o, const char *attr_name)获取sys.path对象,并利用
int PyList_Append(PyObject *list, PyObject *item)将当前路径.添加到sys.path中,以便加载
当前的Python模块(Python文件即python模块)4、使用PyObject *PyImport_ImportModule(const char *name)函数导入Python模块,并检查是否
有错误。
5、使用PyObject *PyObject_GetAttrString(PyObject *o, const char *attr_name)函数获取
Python函数对象,并检查是否可调用。
+6、使用PyObject *Py_BuildValue(const char *format, ...)函数将C类型的数据结构转换成
Python对象,作为Python函数的参数,没有参数不需要调用
7、使用PyObject *PyObject_CallObject(PyObject *callable, PyObject *args)函数调用
Python函数,并获取返回值。
+8、使用int PyArg_Parse(PyObject *args, const char *format, ...)函数将返回值转换为C类
型,并检查是否有错误,没有返回值时不需要调用。
9、使用void Py_DECREF(PyObject *o)函数释放所有引用的Python对象。
10、结束时调用void Py_Finalize()函数关闭Python解释器。
相关的函数参数说明参考网站(网站左上角输入函数名即可开始搜索):
https://docs.python.org/zh-cn/3/c-api/import.html
#endif

#include <Python.h>
#include "garbage.h"


void garbage_init(void)
{
    Py_Initialize();
    // 将当前路径添加到sys.path中
    PyObject *sys = PyImport_ImportModule("sys");//初始化python解释器
    PyObject *path = PyObject_GetAttrString(sys, "path");
    PyList_Append(path, PyUnicode_FromString("."));
}

void garbage_final(void)
{
    //释放所有引用的Python对象
    Py_Finalize();
}
char *garbage_category(char *category)
{
    // 导入para模块
    PyObject *pModule = PyImport_ImportModule("garbage");//引用的是本目录底下的garbage.py文件
    if (!pModule)
    {
        PyErr_Print();
        printf("Error: failed to load para.py\n");
        goto FAILED_MODULE;
    }
    //获取 alibaba_garbage 函数对象
    PyObject *pFunc = PyObject_GetAttrString(pModule, "alibaba_garbage");//garbage.py文件里的这个函数 alibaba_garbage
    if (!pFunc)
    {
        PyErr_Print();
        printf("Error: failed to load say_funny\n");
        goto FAILED_FUNC;
    }

    //调用alibaba_garbage函数并获取返回值
    PyObject *pValue = PyObject_CallObject(pFunc, NULL);
    if (!pValue)
    {
        PyErr_Print();
        printf("Error: function call failed\n");
        goto FAILED_VALUE;
    } 
    //将返回值转换为C类型
    char *result = NULL;  //pValue(py) --> result(c)
    if (!PyArg_Parse(pValue, "s", &result))//PyArg_Parse根据指定的格式字符串将 Python 对象转换为 C 变量
    {
        PyErr_Print();
        printf("Error: parse failed\n");
        goto FAILED_RESULT;
    }
    //打印返回值
    printf("pValue=%s\n", result);
    category = (char*)malloc(sizeof(char)*(strlen(result)+1));//要留一个字节给 \0
    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;
}

4.4 PWM sg90舵机

//pwm.h
#ifndef __PWM__H
#define __PWM__H

#define PWM_GARBAGE 7
#define PWM_RECOVERABLE_GARBAGE 5

void pwm_write(int pwm_pin); // 控制PWM ==> 开盖
void pwm_stop(int pwm_pin);  // 控制PWM ==> 关盖

#endif
//pwm.c
#include <wiringPi.h>
#include <softPwm.h>
// 根据公式PWMfreq = (1 × 10^6 )/(100 × range)    1000000/20000=50 Hz  ==> 周期为 1/50 = 0.02s = 20ms
// 要得到PWM频率为50Hz,则range为200,即周期为200步,控制精度相比硬件PWM较低

void pwm_write(int pwm_pin)
{
    pinMode(pwm_pin, OUTPUT);//把引脚设置为输出
    // 软件模拟PWM函数
    softPwmCreate(pwm_pin,0,200);// range设置周期分为200步,周期为20ms
    softPwmWrite(pwm_pin,15); // 200步里面有多少步是高电平 10/200 = 0.05
                              //  15/200 = 0.075
                              // 1.0ms--------------45度; 5.0% 占空比
                              // 1.5ms------------90度; 7.5% 占空比
    delay(1000); //延时1s
    softPwmStop(pwm_pin);//停止,不输出PWM波了
}

void pwm_stop(int pwm_pin)
{
    pinMode(pwm_pin, OUTPUT);
    softPwmCreate(pwm_pin,0,200);
    softPwmWrite(pwm_pin,5);// 200步里面有多少步是高电平 5/200 = 0.025
                            // 0.5ms-------------0度; 2.5% 占空比
    delay(1000);
    softPwmStop(pwm_pin);
}

4.5 OLED` 显示

//myoled.h
#ifndef __MYOLED__H
#define __MYOLED__H
int myoled_init(void);
int oled_show(void *arg);
#endif
//myoled.c
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>
#include "oled.h"
#include "font.h"
//包含头文件
#include "myoled.h"
#define FILENAME "/dev/i2c-3"
static struct display_info disp;

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 failed");
        break;
    }
    disp.font = font2;
    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 e;
}

4.6 main.c

  • 定义 mutex(互斥锁) & cond(条件变量)

  • //main.c 中包含的函数
    detect_process(const char *);//用于检测特定进程是否在运行。
    pget_voice(void *);			 //获取(读)串口中的数据  0x46 //P8 & P10 ==> TXD.5 & RXD.5
    psend_voice(void *);		 //发送数据(写),回传给串口(语音模块) TX RX
    popen_trash_can(void *);     //通过 buffer[2]的数据,控制舵机,从而开盖
    poled_show(void *);		     //调用 myoled 模块,通过buffer[2]的数据在oled上显示不同数据
    pcategory(void *);           //调用阿里云进行识别、通过返回值来对buffer[2] 进行赋值;并且创造3个线程
    							 //开垃圾桶开盖popen_trash_can、语音播报psend_voice、`oled`显示线程poled_show  
    pget_socket(void *);         //开启socket网络线程 和 心跳包
    main(int, char*[]);
    
  • main.c

//main.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <wiringPi.h>
#include <pthread.h>

#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
#include "socket.h"

#define IPADDR "192.168.1.76"
#define IPPORT "8000"//一定不能是8080,摄像头服务已经使用

int serial_fd = -1;//全局变量
pthread_cond_t cond;
pthread_mutex_t mutex;
// 用于检测特定进程是否在运行。
// 它通过调用系统命令ps -ax | grep process_name|grep -v grep来查找包含指定进程名的进程,并返回相应的进程号。
static int detect_process(const char *process_name)
{
    int n = -1; // 初始化进程号为-1
    FILE *strm; // 文件流指针
    char buf[128] = {0}; // 缓冲区

    // 构建命令字符串,查找特定进程名的进程
    sprintf(buf, "ps -ax | grep %s|grep -v grep", process_name);

    // 执行系统命令,并返回文件指针用于读取命令输出
    if ((strm = popen(buf, "r")) != NULL) 
    {
        // 读取命令输出到缓冲区中
        if (fgets(buf, sizeof(buf), strm) != NULL)
        {
            printf("buf = %s\n", buf); // 输出读取到的内容
            n = atoi(buf); // 将读取到的内容转换为整数,这里假设内容是进程号
            printf("n=%d\n", n); // 输出转换后的进程号
        }
    }
    else
    {
        return -1; // 如果执行popen失败,则返回-1
    }

    pclose(strm); // 关闭文件流
    return n; // 返回进程号,可能为-1(未找到进程或出现错误)
}


void *pget_voice(void *arg)
{
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    int len = 0;
    if(serial_fd == -1)//是否打开成功
    { 
        printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);//文件名-函数名-行号
        pthread_exit(0);
    }
    printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
    while(1) 
    {
        //获取(读)传过来的数据
        len = serialGetstring(serial_fd,buffer); //P8 & P10 ==> TXD.5 & RXD.5
        printf("%s|%s|%d, len=%d\n", __FILE__, __func__, __LINE__,len);
        if(len>0 && buffer[2]==0x46) //0x46命令:识别垃圾类型
        {
            printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
            //上锁
            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(serial_fd == -1)//是否打开成功
    { 
        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;
    //这意味着线程结束时会自动释放其资源,
    //而不需要其他线程调用 pthread_join() 来等待其结束并回收资源
   
    if(buffer[2] == 0x41)
    {
        pwm_write(PWM_RECOVERABLE_GARBAGE); //对应wPi 5口,接着一个舵机
        printf("---------%x---------\n",buffer[2]);
        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);  //对应wPi 7口,接着另一个舵机
        delay(2000);
        pwm_stop(PWM_GARBAGE);
    }
    pthread_exit(0);
}

void *poled_show(void *arg)
{
    pthread_detach(pthread_self()); //这意味着线程结束时会自动释放其资源,
    myoled_init();
    oled_show(arg); //buffer

    pthread_exit(0);
}

void *pcategory(void *arg)
{
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    char *category = NULL;
    pthread_t send_void_tid,trash_tid,oled_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;
        //define WGET_CMD "wget http://127.0.0.1:8080/?action=snapshot -O /tmp/garbage.jpg" //抓拍命令
        system(WGET_CMD);//宏定义:拍照命令,garbage.h 文件中
        // access函数是一个用于测试文件访问权限的函数,它通常用于检查某个文件是否可以被当前用户读取、写入或执行。
        // 包括F_OK(检查文件是否存在)、R_OK(检查读权限)、W_OK(检查写权限)和X_OK(检查执行权限)
        // 该函数返回值为0表示有权限,-1表示无权限或发生错误。
        if(0 == access(GARBAGE_FILE, F_OK))//GARBAGE_FILE 宏定义 识别到的垃圾图片是否存在
        {
            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_void_tid,NULL,popen_trash_can,(void*)buffer);
        
        pthread_create(&oled_tid,NULL,poled_show,(void*)buffer);

        // buffer[2]=0x00;//将buffer[2]清零
        remove(GARBAGE_FILE);//garbage.h 的宏定义 把识别到的垃圾的图片清除掉
    }
    pthread_exit(0);
} 

void *pget_socket(void *fd)
{
    int s_fd = -1;
    int c_fd = -1;
    char buffer[6];
    int nread = -1;
    struct sockaddr_in c_addr;//用于accept

    memset(&c_addr,0,sizeof(struct sockaddr_in));//清零
    s_fd = socket_init(IPADDR,IPPORT);
    printf("%s|%s|%d: s_fd=%d\n",__FILE__,__func__,__LINE__,s_fd);
    if(-1 == s_fd)
    {
        pthread_exit(0);
    }
    sleep(3);
    int clen = sizeof(struct sockaddr_in);

    while(1)
	{
        //accep第三个参数要求是指针//不连接,便堵塞,直到与客户端连接
		c_fd = accept(s_fd, (struct sockaddr *)&c_addr,&clen);
        // 心包跳
        int keepalive = 1; // 开启TCP KeepAlive功能
        int keepidle = 5; // tcp_keepalive_time 3s内没收到数据开始发送心跳包
        int keepcnt = 3; // tcp_keepalive_probes 每次发送心跳包的时间间隔,单位秒
        int keepintvl = 3; // tcp_keepalive_intvl 每3s发送一次心跳包
        setsockopt(c_fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive,
        sizeof(keepalive));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPIDLE, (void *) &keepidle, sizeof
        (keepidle));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcnt, sizeof
        (keepcnt));
        setsockopt(c_fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepintvl, sizeof
        (keepintvl));


        printf("--------ok?--------\n");
        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));
        printf("-----------ok!-----\n");
        if(c_fd == -1)
		{
			perror("accept");//终端打出错误:accept:---
            continue;
		}

        while(1)
        {
            memset(buffer,0,sizeof(buffer));
            nread = recv(c_fd,buffer,sizeof(buffer),0);// == n_read = read(c_fd,readBuf,sizeof(buffer));
            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);
                }
            }
            else if(0==nread || -1==nread)
            {
                break;
            }
        }
        close(c_fd);
    }
    pthread_exit(0);
}

int main(int argc, char *argv[])
{
    printf("adsfasdf\n");
    int len = 0;
    int ret = -1;//标识
    char *category = NULL;
    unsigned char buffer[6] = {0xAA,0x55,0x00,0x00,0x55,0xAA};//AA 55 ** 00 55 AA
    pthread_t get_voice_tid, category_tid,get_socket_tid;

    //初始化
    wiringPiSetup();//初始化wiringPi库
    garbage_init(); //初始化阿里云接口
    
    // 用于检测特定进程是否在运行。
    // 它通过调用系统命令ps -ax | grep process_name|grep -v grep来查找包含指定进程名的进程,并返回相应的进程号  
    // mjpg_streamer 是一款开源的流媒体服务器软件,它能够从一系列输入插件(如USB摄像头)捕获视频数据,并通过网络以MJPG(Motion JPEG)格式实时传输
    ret = detect_process("mjpg_streamer");
    if(-1 == ret)
    {
        printf("detect process failed\n");
        goto END;
    }

    //打开设备
    serial_fd = myserialOpen(SERIAL_DEV,BAUD);//可代替设备节点 & 波特率   返回值fd
    if(serial_fd == -1)//是否打开成功
    {
        goto END;    
    }//*pget_voice(void *arg)里面判断
    

    //printf("%s|%s|%d:open serial failed\n",__FILE__,__func__,__LINE__);
    //开语音的线程
    pthread_create(&get_voice_tid,NULL,pget_voice,NULL);
    //开网络线程
    pthread_create(&get_socket_tid,NULL,pget_socket,NULL);
    //开阿里云交互线程
    pthread_create(&category_tid,NULL,pcategory,NULL);
        //get_voice_tid获取语音模块的数据
        //pthread_join用于等待指定的线程结束其执行
    pthread_join(get_voice_tid,NULL);
    pthread_join(category_tid,NULL);
    pthread_join(get_socket_tid,NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    close(serial_fd);
END:
    garbage_final; //释放所有引用的Python对象
    return 0;
}

5、实验流程和执行结果

5.1 配置客户端和mjpg_Stream

  • 利用网络调试助手

在这里插入图片描述

  • 查看Window & linux主机ip

    window IP 查询方法
    在这里插入图片描述
    Linux orangepi IP 查询方法
    在这里插入图片描述

5.2 调试和运行

>>> gcc -o category *.c *.h -I /usr/include/python3.10/ -lpython3.10 -lwiringPi
//得到 category 可执行文件
  • 输入命令sudo -E ./category
1
在这里插入图片描述
2
在这里插入图片描述

5.3 开始识别

  • 一切都准备好了,现在可以开始实验了

  • 识别干垃圾(纸巾)
    在这里插入图片描述
    识别结果
    在这里插入图片描述
  • 识别可回收垃圾
    在这里插入图片描述
    识别结果
    在这里插入图片描述
  • 识别湿垃圾(一个橘子)
    在这里插入图片描述
    识别结果
    在这里插入图片描述

6、总结

  • 在这个基于全志H616 ARM-Linux 的智能分拣项目中,我们利用的是网络调试助手来进行pcorangepi通信,也就是说,我们可以在实现基于 TCP的socket编程 的远程PC控制;只要orangepiPC机ip是互通的,便可以进行远程操控;

  • 当然我们也可以通过蓝牙模块使得移动端orangepi进行通信,从而实现移动设备对硬件的控制;但是相比 网络通信,蓝牙通信就显得太局限了,无法做到远距离控制;

  • 我们也可以不用阿里云端识别服务,自己来训练自己的模型,然后写入orangpi里面,从而实现自己想要的智能识别功能;



欢迎大家一起交流讨论!

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

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

相关文章

【TomCat】安装部署

首先得进行Java的安装和部署java1.8对应tomcat9 TomCat下载Apache Tomcat - Apache Tomcat 10 Software Downloads

Vue(四) 组件、单文件组件、非单文件组件,重要的内置关系

文章目录 1. 组件2. 非单文件组件2.1 定义组件2.2 注册组件2.3 使用组件2.4 组件命名、标签等注意点2.5 组件嵌套2.6 VueComponent构造&#xff08;这部分看视频更易理解&#xff09;2.7 内置关系 3. 单文件组件 1. 组件 组件是实现局部功能代码和资源的集合 传统方式&#x…

unreal engine5.4.3动画重定向

UE5系列文章目录 文章目录 UE5系列文章目录前言 前言 ue5.4和ue3动画重定向之间存在差异&#xff0c;跟ue5.2差别更大一点&#xff0c;总之ue5.4越来越简化动画重定向&#xff0c;不想之前还需要制作RTG文件 这是ue5.3.2的制作动画重定向的界面 这是ue5.4.2的制作动画重定向…

华芯邦获AEC-Q车规级系列认证与AQG324认证的双重背书,碳化硅SiC-MOSFET半导体功率器件器件已在重点头部新能源汽车厂商模块验证中。

在获得AEC-Q车规级系列认证与AQG324认证的双重背书后&#xff0c;华芯邦的碳化硅SiC-MOSFET器件不仅巩固了其在新能源汽车领域的领先地位&#xff0c;更进一步加速了其在全球汽车供应链中的深度布局。这款车规级产品凭借其卓越的耐高温、低导通电阻及高开关频率等特性&#xff…

kubernetes里面那些事——————OpenEBS

OpenEBS存储 一&#xff0c;OpenEBS简介二&#xff0c;卷类型三&#xff0c;本地卷存储引擎类型四&#xff0c;复制卷存储引擎类型&#xff08;副本卷&#xff09;4.1 复制卷实现原理4.2 复制卷的优势 五&#xff0c;openebs存储引擎技术选型六&#xff0c;k8s中部署openebs服务…

项目延期,怎么有效调整计划,追赶进度

在项目管理过程中&#xff0c;项目延期是一个令人头疼但又无法完全避免的问题。无论是由内部因素还是外部因素导致&#xff0c;都会对项目的整体进度、成本和质量产生影响。 面对延期&#xff0c;关键在于如何迅速而有效地调整项目计划&#xff0c;以确保项目能够尽快回到正轨…

新版IDEA配置前进和后退、打开资源管理器等快捷按钮

新版IDEA&#xff0c;好像是IDEA2024版本开始就默认隐藏了工具条&#xff0c;这时一些很常用的快捷按钮&#xff0c;如前进、后退、打开资源管理器就无法使用。这里图文介绍&#xff0c;如何把这些配置出来。 具体操作如下&#xff1a; 1、选择 File / Settings(windows版)&am…

关于contextmenu-ui组件库

关于这个组件库把&#xff0c;主要用在个人博客制作中&#xff0c;而且由于作者很懒&#xff0c;已经一个多月没有更新了&#xff0c;甚至第二次更新都忘了修改md文件了。 这个组件库是使用vue3和TS来写的&#xff0c;关于引用&#xff0c;看这里 都有介绍的&#xff0c;直接…

计算机网络(八股文)

这里写目录标题 计算机网络一、网络分层模型1. TCP/IP四层架构和OSI七层架构⭐️⭐️⭐️⭐️⭐️2. 为什么网络要分层&#xff1f;⭐️⭐️⭐️3. 各层都有那些协议&#xff1f;⭐️⭐️⭐️⭐️ 二、HTTP【重要】1. http状态码&#xff1f;⭐️⭐️⭐️2. 从输入URL到页面展示…

边缘计算工业网关可以为工业企业生产提供哪些价值应用?天拓四方

在数字化、网络化、智能化高度融合的今天&#xff0c;工业领域正迎来一场深刻的变革。边缘计算工业网关&#xff0c;作为这场变革中的关键角色&#xff0c;以其强大的数据处理能力、高效的通信效率和灵活的部署方式&#xff0c;为智能制造注入了新的活力。本文将结合一个实际应…

【安卓13】解决HDMI OUT和耳机等设备接入时会解除静音问题

安卓原生定义了部分外部设备接入时是否静音&#xff0c;比如耳机、有线扬声器、HDMIOUT设备等&#xff0c;这些设备接入时&#xff0c;安卓会设置AudioSystem.STREAM_MUSIC为非静音状态 从代码里可以看出&#xff0c;当一个newDevice 接入时&#xff0c;会携带一个deviceID&am…

Java基于微信小程序的超市购物管理系统

1 简介 Java基于微信小程序的超市购物管理系统&#xff0c;此超市购物系统利用当下成熟完善的springboot框架&#xff0c;使用跨平台的可开发大型商业网站的Java语言&#xff0c;以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了收货地址管理、购物车管理、…

Win11 / Win10 系统极化工具,降低游戏延迟效果明显

Win11 / Win10 系统优化工具,降低游戏延迟效果明显 Windows 系统优化就是精简系统一些功能组件、对一些系统功能进行设置等&#xff0c;这样可以减少不必要的硬件资源占用。 全面的系统优化功能外&#xff0c;据不少网友表示通过优化后 CS GO 游戏降低输入延迟效果明显。 免费…

领夹麦克风哪个牌子音质好?西圣、博雅、枫笛领夹麦克风对比

当今的直播、短视频已经深深的融入到了我们的生活当中&#xff0c;很多小伙伴会通过拍摄短视频、Vlog来分享自己生活精彩的瞬间。不过录制视频时&#xff0c;如果单纯靠手机拾音会发现&#xff0c;音频效果是极差的&#xff0c;特别距离手机越远效果会越明显&#xff0c;会出现…

【从问题中去学习k8s】k8s中的常见面试题(夯实理论基础)(二十二)

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

一文直接搞懂SpringMVC完整版教程

三、SpringMVC 1、SpringMVC简介 1.1、什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M&#xff1a;Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类&#xff1a; 一类称为实体…

《黑神话悟空》广智打法技巧图文攻略详解

​黑神话悟空广智会挥舞火刀砍我们&#xff0c;是前期一个较难的boss&#xff0c;那么广智怎么打&#xff1f;基本就是别贪刀&#xff0c;躲技能&#xff0c;然后砍就完事儿了。这个boss之所以是第一个boss&#xff0c;可能就是锻炼大家躲技能的能力的&#xff0c;下面就来看下…

大数据测试知识架构与技术框架分享|大数据测试工程师学习方向

本文一起看一下关于大数据技术的基本的技术体系和典型技术栈&#xff0c;帮助想要从事大数据方向软件测试的朋友快速了解需要掌握的知识架构。 大数据基本的思想、它的整体框架&#xff0c;与以往的数据相关体系实际上是相类似的。区别主要在于数据的收集、存储包括资源的调度…

怎么一键导出多条数据的二维码?一物一码的在线生成技巧

怎么将多条数据生成单独二维码呢&#xff1f;现在扫描物品包装的二维码经常会看到对应的产品编号&#xff0c;一般是数字和字母组合内容&#xff0c;每个物品的编码都是不同的&#xff0c;怎么把不同的信息批量生成二维码呢&#xff1f;下面来给大家分享一物一码的使用技巧&…