https://pan.baidu.com/s/1wnXm2uqmt99FOMuyvwre7w 提取码:sail
指令来源于客户端 指令来源于语音模块
可参考下面这篇博文修改orangePi的时间为当前地区时间
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/134175776
打开该路径下新创建的“history.txt” ,可见历史记录已追加的方式不断写入
void *precord(void *arg) //负责每隔一段时间写入历史记录的线程
{
//包含两个整数字段 tv_sec 和 tv_usec 的结构体,分别表示秒和微秒,常用于测量时间间隔。类型分别为 time_t 和 suseconds_t
struct timeval startTime;
struct timeval stopTime;
double diffTime;
int hist_fd; // file description
int ret=-1;
while(1){
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
gettimeofday(&startTime,NULL);//获取当前的时间作为开始时间
while(1){
gettimeofday(&stopTime,NULL);
diffTime = (stopTime.tv_sec - startTime.tv_sec) + 1/1000000 *(stopTime.tv_usec - startTime.tv_usec); //时间间隔单位为秒
if(diffTime > 25){//如果时间是2分钟,由于线程的竞争机制,不一定会非常精确,所以使用>号
//printf("创建文件:%s|%s|%d\n", __FILE__, __func__, __LINE__);
hist_fd = open("./history.txt",O_RDWR|O_CREAT|O_APPEND, 0666); //可读可写可打开的打开历史记录的文件,不存在就创建,且每次都追加写入
if(hist_fd < 0){//打开文件失败
printf("创建文件失败:%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("fail to open history file!\n");
fflush(stdout);//函数打印数据到屏幕时,数据首先会被写入缓冲区,等到缓冲区满或者遇到换行符才真正输出到屏幕上。如果程序需要立即显示某些重要信息,比如错误消息,那么就需要使用 fflush 将缓冲区的内容强行刷新到屏幕
}
ret = write(hist_fd, &hist_whole, strlen(hist_whole));//将数据写入./history.txt
//printf("%s|%s|%d:ret-write=%d\n", __FILE__, __func__, __LINE__, ret);
if(ret == -1){//写入失败
printf("写入失败%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("fail to write history write to file!\n");
fflush(stdout);
}else{//写入成功
//printf("写入成功:%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("write the following history write to file:\n");
printf("------------------------\n");
printf("%s",hist_whole);//打印全部历史数据
printf("------------------------\n");
fflush(stdout);
}
close(hist_fd);//读写完成关闭文件
memset(hist_whole,'\0',sizeof(hist_whole)); //清空hist_whole
break;
}
}
}
pthread_exit(NULL);
}
char hist[128] = {0}; //用于存放一条历史记录,注意这个变量一定不能在record里赋值,因为局部指针变量作为函数的返回值没有意义,会报错; 且作为strcat的对象必须是字符串变量
char hist_whole[10000] = {0}; //用于存放所有历史记录; 且作为strcat的对象必须是字符串变量i
char *record_type(int type){ //用于生成一条历史记录的函数
//type的值1;2;分别对应语音指令识别垃圾类型,客户端指令识别垃圾类型
FILE *fp;//文件流
char *sond = "||voice command||";
char *sock = "||client request||";
memset(&hist,'\0',sizeof(hist));//将存放历史记录的指针清零
fp = popen("date +\"%Y-%m-%d %H:%M:%S\"","r");//打印系统当前时间
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
fread(&hist, sizeof(char), 1024, fp);//读取数据
//指令来源
if(type == 1){ //语音指令
strcat(hist,sond);
printf("%s|%s|%d:语音指令\n", __FILE__, __func__, __LINE__);
}else if(type == 2){ //客户端
strcat(hist,sock);
printf("%s|%s|%d:客户端输入指令\n", __FILE__, __func__, __LINE__);
}
//将一个字符串添加到另一个字符串的末尾
//strcat(hist,c);//每条历史记录结束加上一个换行键
return (char *)hist;
}
char *record_waste(int waste){ //用于生成一条历史记录的函数
//waste的值1;2;3;4;5分别对应干、湿、可回收、有害垃圾,无法识别的垃圾类型
FILE *fp;//文件流
char *dry = "dry waste||";
char *wet = "wet waste||";
char *recy="recyclable waste||";
char *haza="hazardous waste||";
char *reco="recognition failed||";
char *c = "\n";
memset(&hist,'\0',sizeof(hist));//将存放历史记录的指针清零
printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
//垃圾类型
if(waste == 1){
strcat(hist,dry);
}else if(waste == 2){
strcat(hist,wet);
}else if(waste == 3){
strcat(hist,recy);
}else if(waste == 4){
strcat(hist,haza);
}else if(waste == 5){
strcat(hist,reco);
}
//将一个字符串添加到另一个字符串的末尾
strcat(hist,c);//每条历史记录结束加上一个换行键
return (char *)hist;
}
因为指令来源显示在前,垃圾类型显示在后(执行顺序也是),在record-waste函数里面也要将hist数组memset(清空),不然打印的时候会显示两条指令来源(指令来源执行了两次strcat(hist_shole,hist)),不美观,如下:
popen()函数 用于创建一管道,其内部实现为调用fork()产生一个子进程,执行一个shell以运行命令来开启一个进程,这个进程必须由pclose()函数关闭
strcat()函数的主要功能是将一个字符串添加到另一个字符串的末尾。该函数会将第二个参数指向的字符串(src)复制到第一个参数指向的字符串(dest)的末尾,然后返回新的字符串地址。
需要注意的是,`strcat()`函数不会在源字符串后面自动添加空字符'\0',因此在使用该函数时需要手动添加空字符来确保正确地结束字符串。另外,在操作过程中,如果目标字符串的空间不足,则可能导致缓冲区溢出等问题,因此在使用`strcat()`函数之前应检查目标字符串是否有足够的空间容纳新的字符串内容。
分别在语音指令输入和客户端指令输入的地方调用记录语音来源函数record_type(),将此次数据保存到单次数据记录数组hist后通过strcat()函数将单次数据添加到历史数据数组中
阿里云函数判断出垃圾类型后调用record_waste()函数进行记录,将此次数据保存到单次数据记录数组hist后通过strcat()函数将单次数据添加到历史数据数组中
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "uartTool.h"
#include "garbage.h"
#include "pwm.h"
#include "myoled.h"
#include "socket.h"
static int detect_process(const char *process_name){//检测是否已经开起来mjpg-streamer
int n=-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){
n=atoi(buf);
}
}
else{
return -1;
}
pclose(strm);
return n;
}
#if 0
int main(int argc,char *argv[]){
int serial_fd =-1;
unsigned char buffer[6]={0xAA,0X55,0X00,0X00,0X55,0XAA};//串口发送数据起始位是AA 55,停止位是55 AA
//初始化阿里云接口
garbage_init();//打开Python解释器
int ret=-1;
ret=detect_process("mjpg_streamer");
if(-1==ret){
goto END;
}
//orangepi串口初始化
serial_fd=myserialOpen(SERIAL_DEV,BAUD);
if(serial_fd==-1){//判断是否打开成功
goto END;
}
//orangepi读取语音模块数据
while(1){
int len=0;//用于存放串口接收数据函数的返回值
//int serialGetstring (const int fd, unsigned char *buffer);
len=serialGetstring(serial_fd,buffer);
if(len>0 && buffer[2]==0x46){//判断是否接收到开始识别指令
buffer[2]=0x00;//接收到开始识别指令后清零
system(WGET_CMD);//在终端输出拍照指令使摄像头拍照
if(access(GARBAGE_FILE,F_OK)==0){//判断照片是否存在
char *category=NULL;//用于存储返回值(垃圾类型)
//char *garbage_category(char *category);阿里云接口函数
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;
}
//void serialSendstring (const int fd, const unsigned char *s,int len)
serialSendstring(serial_fd,buffer,6);//将内容回传给语音模块,用于播报垃圾类型
if(buffer[2]==0x43){
pwm_start(PWM_RWCYCLABEL);
delay(5000);
pwm_stop(PWM_RWCYCLABEL);
}
buffer[2]=0x00;
remove(GARBAGE_FILE);//清除本次照片,为下一次拍照留空间
}
}
close(serial_fd);
END:
garbage_final();//关闭(释放)Python解释器
return 0;
}
#endif
int serial_fd =-1;//全局变量,在线程函数里面也需要用到fd
char hist[128] = {0}; //用于存放一条历史记录,注意这个变量一定不能在record里赋值,因为局部指针变量作为函数的返回值没有意义,会报错; 且作为strcat的对象必须是字符串变量
char hist_whole[10000] = {0}; //用于存放所有历史记录; 且作为strcat的对象必须是字符串变量i
pthread_cond_t cond;//互斥锁
pthread_mutex_t mutex;//条件变量
pthread_t get_voice_tid,category_tid,get_socket_tid,record_tid;
char *record_type(int type){ //用于生成一条历史记录的函数
//type的值1;2;分别对应语音指令识别垃圾类型,客户端指令识别垃圾类型
FILE *fp;//文件流
char *sond = "||voice command||";
char *sock = "||client request||";
memset(&hist,'\0',sizeof(hist));//将存放历史记录的指针清零
fp = popen("date +\"%Y-%m-%d %H:%M:%S\"","r");//打印系统当前时间
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
fread(&hist, sizeof(char), 1024, fp);//读取数据
//指令来源
if(type == 1){ //语音指令
strcat(hist,sond);
printf("%s|%s|%d:语音指令\n", __FILE__, __func__, __LINE__);
}else if(type == 2){ //客户端
strcat(hist,sock);
printf("%s|%s|%d:客户端输入指令\n", __FILE__, __func__, __LINE__);
}
//将一个字符串添加到另一个字符串的末尾
//strcat(hist,c);//每条历史记录结束加上一个换行键
return (char *)hist;
}
char *record_waste(int waste){ //用于生成一条历史记录的函数
//waste的值1;2;3;4;5分别对应干、湿、可回收、有害垃圾,无法识别的垃圾类型
FILE *fp;//文件流
char *dry = "dry waste||";
char *wet = "wet waste||";
char *recy="recyclable waste||";
char *haza="hazardous waste||";
char *reco="recognition failed||";
char *c = "\n";
memset(&hist,'\0',sizeof(hist));//将存放历史记录的指针清零
printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
//垃圾类型
if(waste == 1){
strcat(hist,dry);
}else if(waste == 2){
strcat(hist,wet);
}else if(waste == 3){
strcat(hist,recy);
}else if(waste == 4){
strcat(hist,haza);
}else if(waste == 5){
strcat(hist,reco);
}
//将一个字符串添加到另一个字符串的末尾
strcat(hist,c);//每条历史记录结束加上一个换行键
return (char *)hist;
}
void *pget_voice(void *arg){
int len=0;
unsigned char buffer[6]={0xAA,0X55,0X00,0X00,0X55,0XAA};//串口发送数据起始位是AA 55,停止位是55 AA
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);
if(len>0 && buffer[2]==0x46){//判断是否接收到开始识别指令
record_type(1);//语音开盖
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
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());//与pcategory父线程分离,防止父线程等待时间过长,子线程执行完后自行释放资源
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);//将内容回传给语音模块,用于播报垃圾类型
printf("Serial get string success!\n");
}else{
printf("%s|%s|%d:sendstring NULL!\n",__FILE__,__func__,__LINE__);
}
pthread_exit(0);
}
void *popen_trash(void *arg){
pthread_detach(pthread_self());//与pcategory父线程分离,防止父线程等待时间过长,子线程执行完后自行释放资源
unsigned char *buffer=(unsigned char *)arg;
if(NULL!=buffer){
if(buffer[2]==0x43){
pwm_start(PWM_RWCYCLABEL);
delay(2000);
pwm_stop(PWM_RWCYCLABEL);
}
}
pthread_exit(0);
}
void *pshow_oled(void *arg){//oled显示垃圾类型
pthread_detach(pthread_self());
myoled_init();
oled_show(arg);
pthread_exit(0);
}
void *pcategory(void *arg){
pthread_t send_voice_tid,trash_tid,oled_tid;//语音播报、垃圾桶开盖和OLED显示线程ID
char *category=NULL;//用于存储返回值(垃圾类型)
unsigned char buffer[6]={0xAA,0x55,0x00,0x00,0x55,0xAA};//串口发送数据起始位是AA 55,停止位是55 AA
while(1){
pthread_mutex_lock(&mutex);//加锁
pthread_cond_wait(&cond,&mutex);//等待触发信号
pthread_mutex_unlock(&mutex);//解锁
//buffer[2]=0x00;
system(WGET_CMD);//在终端输出拍照指令使摄像头拍照
if(access(GARBAGE_FILE,F_OK)==0){//判断照片是否存在
//char *garbage_category(char *category);阿里云接口函数
category=garbage_category(category);
if(strstr(category,"干垃圾")){
buffer[2]=0x41;
record_waste(1);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}else if(strstr(category,"湿垃圾")){
buffer[2]=0x42;
record_waste(2);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}else if(strstr(category,"可回收垃圾")){
buffer[2]=0x43;
record_waste(3);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}else if(strstr(category,"有害垃圾")){
buffer[2]=0x44;
record_waste(4);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}else{
buffer[2]=0x45;
record_waste(5);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}
}else{
buffer[2]=0x45;
record_waste(5);
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
}
//语音播报线程
pthread_create(&send_voice_tid,NULL,psend_voice,(void *)buffer);
//垃圾桶开盖线程
pthread_create(&trash_tid,NULL,popen_trash,(void *)buffer);
//OLED显示进程
pthread_create(&oled_tid,NULL,pshow_oled,(void *)buffer);
//因为做了父子线程分离,所以无需执行pthread_join()释放资源
remove(GARBAGE_FILE);//清除本次照片,为下一次拍照留空间
}
pthread_exit(0);
}
void *pget_socket(void *arg){
int s_fd=-1;
int c_fd=-1;
char buffer[6];
int n_read=-1;
struct sockaddr_in c_addr;
memset(&c_addr,0,sizeof(struct sockaddr_in));
s_fd=socket_init(IPADDR,IPPORT);//IP地址和端口号
if(-1==s_fd){
printf("%s|%s|%d:s_fd=%d\n", __FILE__, __func__, __LINE__, s_fd);
pthread_exit(0);
}
int len=sizeof(struct sockaddr_in);
while(1){
c_fd=accept(s_fd,(struct sockaddr*)&c_addr,&len);
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("%s|%s|%d: Accep aconnection 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));
n_read=recv(c_fd,buffer,sizeof(buffer),0);
printf("%s|%s|%d:nread=%d, buffer=%s\n", __FILE__, __func__, __LINE__, n_read, buffer);
if (n_read > 0){
if (strstr(buffer, "start")){
record_type(2);//客户端指令
strcat(hist_whole,hist);//在总的历史记录末尾加上一条刚刚生成的记录
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
}
}
else if(0 == n_read || -1 == n_read){
break;
}
}
close(c_fd);
}
pthread_exit(0);
}
void *precord(void *arg) //负责每隔一段时间写入历史记录的线程
{
//包含两个整数字段 tv_sec 和 tv_usec 的结构体,分别表示秒和微秒,常用于测量时间间隔。类型分别为 time_t 和 suseconds_t
struct timeval startTime;
struct timeval stopTime;
double diffTime;
int hist_fd; // file description
int ret=-1;
while(1){
//printf("%s|%s|%d\n", __FILE__, __func__, __LINE__);
gettimeofday(&startTime,NULL);//获取当前的时间作为开始时间
while(1){
gettimeofday(&stopTime,NULL);
diffTime = (stopTime.tv_sec - startTime.tv_sec) + 1/1000000 *(stopTime.tv_usec - startTime.tv_usec); //时间间隔单位为秒
if(diffTime > 25){//如果时间是2分钟,由于线程的竞争机制,不一定会非常精确,所以使用>号
//printf("创建文件:%s|%s|%d\n", __FILE__, __func__, __LINE__);
hist_fd = open("./history.txt",O_RDWR|O_CREAT|O_APPEND, 0666); //可读可写可打开的打开历史记录的文件,不存在就创建,且每次都追加写入
if(hist_fd < 0){//打开文件失败
printf("创建文件失败:%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("fail to open history file!\n");
fflush(stdout);//函数打印数据到屏幕时,数据首先会被写入缓冲区,等到缓冲区满或者遇到换行符才真正输出到屏幕上。如果程序需要立即显示某些重要信息,比如错误消息,那么就需要使用 fflush 将缓冲区的内容强行刷新到屏幕
}
ret = write(hist_fd, &hist_whole, strlen(hist_whole));//将数据写入./history.txt
//printf("%s|%s|%d:ret-write=%d\n", __FILE__, __func__, __LINE__, ret);
if(ret == -1){//写入失败
printf("写入失败%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("fail to write history write to file!\n");
fflush(stdout);
}else{//写入成功
//printf("写入成功:%s|%s|%d\n", __FILE__, __func__, __LINE__);
printf("write the following history write to file:\n");
printf("------------------------\n");
printf("%s",hist_whole);//打印全部历史数据
printf("------------------------\n");
fflush(stdout);
}
close(hist_fd);//读写完成关闭文件
memset(hist_whole,'\0',sizeof(hist_whole)); //清空hist_whole
break;
}
}
}
pthread_exit(NULL);
}
int main(int argc,char *argv[]){
unsigned char buffer[6]={0xAA,0X55,0X00,0X00,0X55,0XAA};//串口发送数据起始位是AA 55,停止位是55 AA
//初始化阿里云接口
garbage_init();//打开Python解释器
int ret=-1;
ret=detect_process("mjpg_streamer");
if(-1==ret){
printf("detect process failed\n");
goto END;
}
wiringPiSetup();
//orangepi串口初始化
serial_fd=myserialOpen(SERIAL_DEV,BAUD);
if(serial_fd==-1){//判断是否打开成功
printf("opne serial failed!\n");
goto END;
}
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
//void *(*start_routine) (void *), void *arg);
//语音模块线程
pthread_create(&get_voice_tid,NULL,pget_voice,NULL);
//阿里云线程
pthread_create(&category_tid,NULL,pcategory,NULL);
//网络通信线程
pthread_create(&get_socket_tid,NULL,pget_socket,NULL);
//动作记录线程
pthread_create(&record_tid,NULL,precord,NULL);
//int pthread_join(pthread_t thread, void **retval);
pthread_join(get_voice_tid,NULL);
pthread_join(category_tid,NULL);
pthread_join(get_socket_tid,NULL);
pthread_join(record_tid,NULL);
pthread_mutex_destroy(&mutex);
pthread_cond_destroy(&cond);
close(serial_fd);
END:
garbage_final();//关闭(释放)Python解释器
return 0;
return 0;
}