【嵌入式Linux项目】基于Linux的全志H616开发板智能垃圾桶项目

目录

一、功能需求

二、涵盖的知识点

1、wiringPi库下的相关硬件操作函数调用

2、线程(未使用互斥锁和条件)

 3、父子进程

4、网络编程(socket套接字)

5、进程间通信(共享内存和信号量)

三、开发环境

1、硬件:

2、软件:

3、引脚分配:

四、代码

1、服务端代码:server.c

2、客户端代码:client.c

五、编译和运行

六、视频功能展示


一、功能需求

  • 靠近时,垃圾桶开盖2秒,2秒后关盖
  • 垃圾桶开盖、关盖带滴滴声(蜂鸣器发声)
  • 垃圾桶开盖超过10秒,滴滴声报警
  • 通过Socket网络编程,开发板运行服务端,上位机运行客户端。实现Socket客户端发送指令远程打开和关闭垃圾桶

二、涵盖的知识点

1、wiringPi库下的相关硬件操作函数调用

包括wiringPi库的初始化,蜂鸣器、超声波测距、sg90舵机的输入输出引脚配置和高低电平设置,超声波测距中的时间函数和距离测算,sg90舵机中的PWM信号控制、Linux定时器和信号处理。

2、线程(未使用互斥锁和条件)

服务端创建了三个线程:超声波测距、sg90舵机、socket命令。(下面括号中为对于线程函数名)

  • 超声波测距(*ultrasonic):在while循环中每隔0.5s计算一次距离。
  • sg90舵机(*sg90):在while循环中每隔20ms通过所测距或客户端指令,执行垃圾桶开盖/关盖。
  • socket命令(*socketCmd):完成共享内存和信号量的创建,在while循环中每隔0.5s查看客户端是否发出指令,若服务端收到指令(open or close),则执行垃圾桶开盖/关盖。

 3、父子进程

在main函数中,父进程完成设备初始化和socket服务端搭建。while循环中,父进程阻塞在accept函数处,等待多个客户端接入;子进程实现对共享内存和信号量的获取,在while循环中读取客户端发出的指令,并将指令写到共享内存。

4、网络编程(socket套接字)

通过Socket网络编程,开发板运行服务端,上位机运行客户端。实现Socket客户端发送指令远程打开和关闭垃圾桶。

5、进程间通信(共享内存和信号量)

父子进程间的通信通过共享内存来完成,信号量用于实现进程间的互斥与同步。

                                                             图1 cmd指令流向图

三、开发环境

1、硬件:

Orangepi Zero2 全志H616开发板,超声波测距模块,蜂鸣器,sg90舵机,一台电脑

2、软件:

MobaXterm、Ubuntu linux操作系统(虚拟机)

3、引脚分配:

在MobaXterm命令控制终端输入gpio readall可以查看开发板上的所有引脚。蜂鸣器、超声波测距和sg90舵机的引脚接线在下图框出。

图2 引脚分配

四、代码

1、服务端代码:server.c

#include <stdio.h>
#include <sys/time.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>      
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>#define Trig 9
#define Echo 10
#define BEEP 0 //设置针脚0为蜂鸣器的控制引脚
#define SG90Pin 6
#define OPEN  1
#define CLOSE 2static int curAngle;//当前角度
static int prevAngel;//上一个角度
static int i 			    = 0;
static int j 			    = 5;
static double distance 		= 0;
static int 	  openCnt 		= 0;
static int    openSocket 	= 0;
static int    closeSocket 	= 0;
char buf[128];struct Msg
{int type;char cmd[128];
};/**************信号量**************/
union semun {int              val;    /* Value for SETVAL */struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */unsigned short  *array;  /* Array for GETALL, SETALL */struct seminfo  *__buf;  /* Buffer for IPC_INFO */
};void pGetKey(int id)
{struct sembuf set;set.sem_num = 0;//信号量编号 不写也可以 默认是0set.sem_op = -1;//把钥匙 -1set.sem_flg=SEM_UNDO;//设置为等待semop(id, &set, 1);//在semop函数中取钥匙//printf("getkey\n");
}void vPutBackKey(int id)
{struct sembuf set;set.sem_num = 0;set.sem_op = 1;//把钥匙 +1 (锁的数量+1)set.sem_flg=SEM_UNDO;semop(id, &set, 1);//printf("put back the key\n");
}/**************服务器控制指令**************/
void *socketCmd(){//父进程 共享内存和信号量创建int shmid;int semid;char *shmaddr;//指向共享内存int retCmp = 0;//key是16进制整数,读写的key相同就会访问同一个共享内存 key_t key1;key_t key2;key1 = ftok(".",1);//“."为当前路径 key值由ftok中两个参数确定,与shmr.c中的key值相同,shmid也相同key2 = ftok(".",2);//共享内存 存储空间大小以 兆为单位对齐//创建或获取一个共享内存:成功返回共享内存ID,失败返回-1//1兆,可读可写的权限shmid = shmget(key1,1024*1,IPC_CREAT|0666);if(shmid == -1){printf("shmget fail\n");exit(-1);}//shmat 共享内存映射:将共享内存挂载到进程的存储空间(连接共享内存到当前进程的地址空间)//成功返回0,失败返回-1//获取的共享内存ID,0为 linux内核为我们自动安排共享内存,0为映射进来的共享内存为可读可写//void *shmat(int shmid, const void *shmaddr, int shmflg);shmaddr = shmat(shmid,0,0);//参数 0 0默认即可//信号量集合中有一个信号量  1为信号量集中信号量的个数semid = semget(key2, 1, IPC_CREAT|0666);//获取/创建信号量union semun initsem;initsem.val = 1;//一开始无锁的状态(无钥匙);若是1,则有锁 //0代表 操作第0个信号量 ,只有1个信号量 ,SETVAL是设置信号量的初值semctl(semid, 0, SETVAL, initsem);//初始化信号量//SETVAL设置信号量的值,设置为inisemwhile(1){//初始化作用://(1)避免程序多次运行,shmaddr是同一个内存空间,这样初始值为open/close//(2)执行open开盖5s后,客户端再输入open,仍可执行pGetKey(semid);strcpy(buf, "\0");strcpy(shmaddr, "\0");vPutBackKey(semid);strcpy(buf, shmaddr);usleep(500000);//每隔0.5s检查retCmp = strcmp(buf, shmaddr);//返回值为0 表示buf和shmaddr内容相同if(retCmp != 0){//客户端输入了指令 open or closeif(!strcmp("open", shmaddr)){//open 开盖5sopenSocket = 1;printf("*******cmd open******\n");}if(!strcmp("close", shmaddr)){//closeif(openSocket == 1){//在客户端执行open指令前提下(5s内),再执行closecloseSocket = 1;printf("*******cmd close******\n");}			}//只有以上两种情况,if语句中的表达式 更具可读性,故第二种情况不用else}	}//销毁锁semctl(semid,0,IPC_RMID);//卸载(断开)共享内存(退出连接):成功返回0,失败返回-1shmdt(shmaddr);//将共享内存释放:成功返回0,失败返回-1shmctl(shmid, IPC_RMID, 0);//保持默认
}/******************蜂鸣器******************/
void beepInit(){pinMode(BEEP, OUTPUT);//设置IO口的输入输出,输出digitalWrite(BEEP, HIGH);
}void beepOn(){digitalWrite(BEEP, LOW);  // low输出低电平,蜂鸣器响
}void beepOff(){digitalWrite(BEEP, HIGH);  // high输出高电平,蜂鸣器不响
}/******************超声波******************/
void ultrasonicInit(){pinMode(Trig, OUTPUT);pinMode(Echo, INPUT);
}double getDistance(){double dis;struct timeval start;struct timeval stop;//pinMode(Trig, OUTPUT);//pinMode(Echo, INPUT);digitalWrite(Trig ,LOW);usleep(5);digitalWrite(Trig ,HIGH);usleep(10);digitalWrite(Trig ,LOW);//超声波未发出前,1号引脚一直处于低电平0,一但发出声波则高电平,跳出循环while(!digitalRead(Echo));gettimeofday(&start,NULL);//超声波回来瞬间,Echo引脚变为低电平,跳出循环while(digitalRead(Echo));gettimeofday(&stop,NULL);//秒*10^6转换成微秒long diffTime = 1000000*(stop.tv_sec-start.tv_sec)+(stop.tv_usec -start.tv_usec);//printf("diffTime = %ld\n",diffTime);dis = (double)diffTime/1000000 * 34000 / 2;//单位厘米return dis;
}void *ultrasonic(){while(1){usleep(500000);//每0.5s计算一次距离distance = getDistance();//printf("distance = %lf cm\n",distance);}
}/*******************舵机******************/
void signal_handler(int signum)
{//curAngle: 1-0° 2-45° 3-90° 4-135°if(i <= curAngle){digitalWrite(SG90Pin, HIGH);}else{digitalWrite(SG90Pin, LOW);} if(i == 40){i = 0;} i++;
}void sg90Init(){struct itimerval itv;curAngle = 4;//角度初始化 关盖prevAngel = 4;pinMode(SG90Pin, OUTPUT);//设定定时时间,每500um触发一次SIGALRM信号itv.it_interval.tv_sec = 0;itv.it_interval.tv_usec = 500;//设定开始生效,启动定时器的时间itv.it_value.tv_sec = 1;itv.it_value.tv_usec = 0;//设定定时方式if( -1 == setitimer(ITIMER_REAL, &itv, NULL)){perror("error");exit(-1);} //信号处理signal(SIGALRM, signal_handler);
}void openLid(){curAngle = 1;if(curAngle != prevAngel){//条件成立:关盖->开盖beepOn();usleep(200000);beepOff();}prevAngel = curAngle;usleep(20000);
}void closeLid(){curAngle = 4;if(curAngle != prevAngel){//条件成立:开盖->关盖beepOn();usleep(200000);beepOff();usleep(100000);beepOn();usleep(200000);beepOff();}prevAngel = curAngle;usleep(20000);
}void *sg90(){while(1){j = 5;usleep(20000);if(distance < 10.0 || openSocket == 1){//printf("=====开盖=====\n");if(openSocket == 1){//开盖5sopenLid();while(j--){//j=5 相当于sleep(5)sleep(1);	if(closeSocket == 1){//客户端发送了close指令goto closeInterrupt;}}openSocket = 0;			}else{openLid();sleep(2);openCnt++;if(openCnt == 5){//连续开盖到达10秒,报警while(distance < 10.0){beepOn();usleep(200000);beepOff();usleep(100000);}}}			}else{//printf("=====关盖=====\n");closeInterrupt:openSocket = 0;closeSocket = 0;openCnt = 0;closeLid();	}}
}void deviceInit(){// == -1 说明库的初始化失败if(wiringPiSetup() == -1){fprintf(stderr,"%s","initWringPi error");exit(-1);}beepInit();ultrasonicInit();sg90Init();sleep(1);/*线程创建成功后,线程ID存放在此pthread_t ultrasonicID;pthread_t sg90ID;wiringPi库下的线程创建
int piThreadCreate (void *(*fn)(void *)){pthread_t myThread ;return pthread_create (&myThread, NULL, fn, NULL) ;
}
*/	int retUltrasonic = piThreadCreate(ultrasonic);	int retSg90 = piThreadCreate(sg90);int retSocketCmd = piThreadCreate(socketCmd);
}/***********运行格式 sudo ./server IP地址 端口号************/
int main(int argc, char **argv)
{int s_fd;int c_fd;int n_read;struct sockaddr_in s_addr;struct sockaddr_in c_addr;struct Msg msg;if(argc != 3){printf("The number of parameters does not match\n");exit(-1);}deviceInit();memset(&s_addr,0,sizeof(struct sockaddr_in));memset(&c_addr,0,sizeof(struct sockaddr_in));//1.sockets_fd = socket(AF_INET, SOCK_STREAM, 0);if(s_fd == -1){perror("socket");exit(-1);}//将协议类型,IP地址,端口号信息放在结构体重s_addr.sin_family = AF_INET;s_addr.sin_port = htons(atoi(argv[2]));//ASCII 转换成 intinet_aton(argv[1],&s_addr.sin_addr);//2. bindbind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));//3. listenlisten(s_fd,10);//4. acceptint clen = sizeof(struct sockaddr_in);while(1){c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);if(c_fd == -1){perror("accept");}printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));if(fork() == 0){	//子进程 获取共享内存和信号量int shmid;int semid;char *shmaddr;//指向共享内存key_t key1;key_t key2;key1 = ftok(".",1);//“."为当前路径 key值由ftok中两个参数确定,与shmr.c中的key值相同,shmid也相同key2 = ftok(".",2);shmid = shmget(key1,1024*1,0);if(shmid == -1){printf("shmget fail\n");exit(-1);}shmaddr = shmat(shmid,0,0);//参数 0 0默认即可semid = semget(key2, 1, IPC_CREAT|0666);	union semun initsem;initsem.val = 1;//一开始无锁的状态(无钥匙);若是1,则有锁semctl(semid, 0, SETVAL, initsem);while(1){memset(msg.cmd, 0, sizeof(msg.cmd));n_read = read(c_fd, &msg, sizeof(msg));	if(n_read == 0){printf("client out\n");break;}else if(n_read > 0){printf("server get msg:%s\n",msg.cmd);//msg_cmd_handler(msg);//指令一来就调用函数pGetKey(semid);strcpy(shmaddr, msg.cmd);//指令放入共享内存vPutBackKey(semid);}}shmdt(shmaddr);}}close(c_fd);close(s_fd);return 0;
}

2、客户端代码:client.c

#include <stdio.h>
#include <sys/types.h>    
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>#define OPEN  0
#define CLOSE 1
#define QUIT 2
#define ERROR 3struct Msg
{int type;char cmd[128];
};int get_cmd_type(char *cmd)
{if(!strcmp("open",cmd))    return OPEN;if(!strcmp("close",cmd))   return CLOSE;if(!strcmp("quit",cmd))   return QUIT;return ERROR;
}void msg_handler(struct Msg msg, int c_fd)
{switch(get_cmd_type(msg.cmd)){case OPEN:case CLOSE:write(c_fd,msg,sizeof(msg));;break;case QUIT:exit(1);case ERROR:printf("wrong cmd\n");break;}
}/***********运行格式 sudo ./client IP地址 端口号************/
int main(int argc, char **argv)
{int c_fd;int n_read;struct sockaddr_in c_addr;struct Msg msg;memset(&c_addr,0,sizeof(struct sockaddr_in));if(argc != 3){printf("The number of parameters does not match\n");exit(-1);}//1. socketc_fd = socket(AF_INET, SOCK_STREAM, 0);if(c_fd == -1){perror("socket");exit(-1);}c_addr.sin_family = AF_INET;c_addr.sin_port = htons(atoi(argv[2]));inet_aton(argv[1],&c_addr.sin_addr);//2.connect	if(connect(c_fd, (struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){perror("connect");exit(-1);}while(1){//子进程 写指令if(fork()==0){while(1){memset(msg.cmd,0,sizeof(msg.cmd));msg.type = 1;printf(">");gets(msg.cmd);//open close alarmmsg_handler(msg,c_fd);}}//父进程读服务端的数据 while(1){//	memset(msg,0,sizeof(msg));n_read = read(c_fd, &msg, sizeof(msg));if(n_read == 0){printf("server is out,quit\n");exit(-1);}		printf("\n%s\n",msg.cmd);//printf("nread=%d\n",n_read);		}}return 0;
}

五、编译和运行

编译服务端和客户端代码:

gcc server.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -o server
gcc client.c -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt -o client

运行服务端和客户端代码:

sudo ./server 127.0.0.1 9999
sudo ./client 127.0.0.1 9999

需要注意的是,开发板与上位机需要同一网段才可通信,在运行程序前先配置好ip地址。

六、视频功能展示

全志H616垃圾桶功能展示

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch原理剖析

一、 Elasticsearch结构 Elasticsearch集群方案由EsMaster、EsClient和EsNode1、EsNode2、EsNode3、EsNode4、EsNode5、EsNode6、EsNode7、EsNode8、EsNode9进程组成&#xff0c;如下图所示&#xff0c;模块说明如表下所示。 说明如表&#xff1a; 名称说明ClientClient使用H…

OpenCV 入门教程:颜色空间转换

OpenCV 入门教程&#xff1a;颜色空间转换 导语一、颜色空间的基本概念1.1 RGB颜色空间1.2 灰度颜色空间1.3 其他颜色空间 二、颜色空间转换三、示例应用3.1 提取图像的色彩通道3.2 调整图像的亮度和对比度 总结 导语 在图像处理和计算机视觉领域&#xff0c;颜色空间转换是一…

OpenCV(图像处理)-图片搜索

图片搜索 1.知识介绍2.实现流程2.1 计算特征点与描述子2.2 描述子的匹配2.3 求出单应性矩阵并画出轮廓2.4 将特征点标出 此篇博客作者仍在探索阶段&#xff0c;还有一些模糊的概念没有弄懂&#xff0c;请读者自行分辨。 1.知识介绍 Opencv进行图片搜索需要的知识有&#xff1…

nginx漏洞修复之检测到目标URL存在http host头攻击漏洞

漏洞说明 为了方便的获得网站域名&#xff0c;开发人员一般依赖于HTTP Host header。例如&#xff0c;在php里用_SERVER[“HTTP_HOST”]。但是这个header是不可信赖的&#xff0c;如果应用程序没有对host header值进行处理&#xff0c;就有可能造成恶意代码的传入。 解决方法…

前端学习——css盒子模型、css3新特性、伪类、布局0711TODO

样式还是得具体使用才能理解&#xff0c;不然会忘记也理解不透彻&#xff1b;还有定位&#xff0c;元素溢出&#xff0c;浮动&#xff0c;布局水平&垂直对齐&#xff1a; css3新特性 1过渡 2 动画 3 2D、3D转换 伪类 三种定位方式 弹性布局/栅格布局

Unity | 向量、矩阵、齐次坐标

目录 一、向量点乘&叉乘 1.点乘 1.1 公式 1.2 几何意义 2.叉乘 2.1 公式 2.2 几何意义 二、矩阵点乘&叉乘 1.矩阵 2.矩阵的点乘 3.矩阵的叉乘 三、矩阵旋转 四、齐次坐标 一、向量点乘&叉乘 1.点乘 又称内积&#xff0c;结果是个标量&#xff0c; 1…

opencv读取图像数据并修改通道转变内存连续

opencv读取图像数据并修改通道转变内存连续

基于vscode的ros开发

Part1前言 ROS&#xff08;机器人操作系统&#xff09;是一个开源的机器人软件平台&#xff0c;旨在提供一套通用的工具和库&#xff0c;帮助开发人员创建机器人应用程序。ROS提供了用于构建机器人系统的库、工具、驱动程序、通信协议和软件包管理系统。 以下是ROS的一些主要特…

安科瑞智能母线监控在数据中心的应用

引言&#xff1a; 近年来&#xff0c;随着母线槽在建筑及工厂的配电中越来越广泛&#xff0c;母线槽场景运用的越多&#xff0c;随着数据中心建设的快速发展和更高需求&#xff0c;智能母线系统逐渐被应用于机房的末端配电中&#xff0c;具有电流小、插接方便、智能化程度高等…

快消品行业企业如何选择适合自己的订单管理系统源码

快消品行业企业在选择适合自己的订单管理系统源码时&#xff0c;需要考虑以下五个方面&#xff1a; 首先&#xff0c;企业需要考虑订单管理系统的功能是否能够满足自身的需求。订单管理系统应该具备订单录入、订单查询、订单处理、订单跟踪、进销存管理、临期提醒等基本功能&am…

sodner 论文复现

论文&#xff1a;A Span-Based Model for Joint Overlapped and DiscontinuousNamed Entity Recognition_pepsi_w的博客-CSDN博客 因为搞这个复现的环境弄了很久才跑通&#xff0c;记录一下。 介绍一下我的环境&#xff1a;window10 cpu pycharm miniconda 1. 下载代码文件&a…

VMware15.5版本虚拟机安装Linux Centos 7系统详细步骤

1.首先准备好Centos7.6安装文件&#xff0c;安装文件可百度搜索或在阿里镜像站中下载。 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 2.新建虚拟机. 1选择自定义&#xff0c;点下一步。 2硬件兼容性选择15.x&#xff0c;&#xff0c;点下一步。 3选择稍后安装操作系统&…