本次项目是基于Linux环境的交叉编译arm-linux-gcc,在GEC6818arm开发板上运行,通过摄像头监控,RFID模块识别卡号,判断该卡是否为月卡or临时卡,车辆入场or出场,记录或显示收费金额与车辆信息。运用SQLite数据库,创建表格存取月卡用户信息与出入车辆相关信息。增加语音播报功能,使项目更加人性化…
(1)创建一个数据库, 内置你需要的所有信息, 如车牌信息、RFID卡信息、卡类、进场时间、车辆照片名等
(2) 在默认状态下,视频监控是处于打开状态的,并循环录制,1分钟视频,保存到本地。
(3)当检测到有RFID卡,关闭视频流,判断当前数据库中是否有该卡的入场信息:
如果已有该卡的入场信息,表示现在是出场;则直接计算出当前时间和入场时间差值,在屏幕上显示该车辆照片,显示车辆信息和应收费金额,停车总时间
如果没有该卡的入场信息,则在数据库中增加该车辆信息,并记录当前时间且拍照。
在检测到RFID卡3秒后 (alarm() SIGALRM信号), 系统会恢复打开视频流
(4)当车辆刷卡进场是,语音提示卡号,并说明卡的类别(临时卡,或包月卡),出场提示:费用与月卡剩余天数
(5)管理功能,负责把卡的类别进行修改,与数据库中的数据进行修改。
(6) 自行扩展: 添加一些自主功能。
(7)强测试代码,把BUG 去掉!!!!
开发环境:
Linux
开发工具:
arm-linux-gcc、Notepad++
其他工具:
SQLite数据库、MIFARE522、ffmpeg库、V4l2编程、ALSA库、mplayer开源多媒体库、jpeglib库、font库、讯飞语音
编程实现:
通过开源代码编译器notepad++编写代码,利用arm-linux-gcc交叉编译,再通过LINUX平台的SSH服务器将编译生成的程序文件传输到开发板中,最后执行。
SQLite数据库
SQLite是目前最流行的开源嵌入式数据库,和很多其他嵌入式存储引擎相比(NoSQL),如
BerkeleyDB、MemBASE等,SQLite可以很好的支持关系型数据库所具备的一些基本特征,如标准SQL语法、事务、数据表和索引等。事实上,尽管SQLite拥有诸多关系型数据库的基本特征,然而由于应用场景的不同,它们之间并没有更多的可比性。
部分代码指令:
往表格中插入数据
语法格式:
INSERT INTO [数据库名称].表名 VALUES(记录内容)
INSERT INTO [数据库名称].表名(字段列表) VALUES(对应字段内容)
例子:往一个表格中插入数据 (一次插入)
insert into my_table values("2019661130");
//指定字段的顺序
insert into my_table2(age,id) values(123456,987654);
查询
查询表格中的数据
select * from table;
查找数据的多种方式:
按照字段去查找数据:
select 字段名 from 表明; //只找出该字段的内容
根据条件去找数据:
select 字段名 from 表明 where 条件; //安装条件去找该字段中的数据
select * from my_table3 where id=123; //找到ID 为 123的所有数据。
多条件查询:
select 字段名 from 表明 where 条件1 or 条件2; //或
select * from my_table3 where id=123 or id=456; //找出表格中 id 为 123 或 456 的数据
修改
更新数据库中的数据:
UPDATE [数据库名称].表名 SET 字段 1=字段 1 值,字段 2=字段 2 值… where 条件表达式
例子:更新ID 信息
update my_table3 set id=10086 where id=123;
删除
删除整个表格;
DROP TABLE 表名;
DROP TABLE tbl_student_info
删除表格中的数据:
DELETE FROM [数据库名称].表名 where 条件表达式
例子:删除表格中的数据
delete from my_table3 where id = 123;
MIFARE522
RFID模块:本模块以命令——响应的方式工作,在系统中模块是处于从属地位,不会主动发出数据(自动检测卡片除外)。通常主机首先发出命令,然后等待模块响应。
实体图如下:
ffmpeg库:
FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。采用LGPL或GPL许可证。它提供了录制、转换以及流化音视频的完整解决方案。
FFmpeg在Linux平台下开发,但它同样也可以在其它操作系统环境中编译运行,包括Windows、Mac OS X等。
V4l2编程:
V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。V4L2 的相关定义包含在头文件
本次项目中利用官方V4L2接口对摄像头进行解码,每一帧生成JPG图片,摄像头实体图如下:
其他工具和库:
参考此篇(3 搭建环境):ALSA库、mplayer开源多媒体库、jpeglib库、font库、讯飞语音
基本:
核心
主要实现:
1.准备入场车辆链表头节点,当车辆入场时,链表节点保存入场图片
2.打开数据库,创建表格:(1)入场车辆;(2)月卡信息
3.初始化摄像头,开启捕捉,实时监控
4.打开串口RFID模块,实时等待读取卡号
5.打开触摸屏,实现其他界面跳转
具体效果和代码如下:
实体效果
部分代码
struct double_list *head = malloc(sizeof(struct double_list)); //已入场车辆相册头节点
head->next = head;
head->prev = head;
head->pic_name[200]=0;
//打开数据库
int ret=sqlite3_open("./my.db",&sql);
//创建表格:车牌信息,、RFID卡信息、卡类、进场时间、车辆照片名
char *sql_cmd="create table my_table(car_id text primary key,card_id text unique,card_style text,date text,car_photo text);";
ret=sqlite3_exec(sql,sql_cmd,NULL,NULL,NULL);
if(ret==SQLITE_ERROR){printf("表格已经存在\n");}
else if(ret==SQLITE_OK){printf("表格创建成功\n"); }
//创建表格..月卡数据库:车牌信息,、RFID卡信息、车主名字,注册月份,注册日期;
char *sql_cmd1="create table month_card(car_id text primary key,card_id text unique,name text,month int(3),day int(3));";
ret=sqlite3_exec(sql,sql_cmd1,NULL,NULL,NULL);
struct jpg_data video_buf;
//定义结构体变量
lcd_open();
mmap_lcd();
linux_v4l2_yuyv_init("/dev/video7"); //初始化摄像头
linux_v4l2_start_yuyv_capturing(); //开启摄像头捕捉
pthread_create(&tid,NULL,get_card_id,NULL);//读卡
pthread_create(&tid1,NULL,get_video,NULL);//监控
pthread_create(&tid2,NULL,get_time,NULL);//当前时间
lcd_fd=open("/dev/input/event0",O_RDWR);//打开触摸屏
int video_open=0;//摄像头标志位
while(1)
{
show_pohoto("jiemian.bmp");//显示界面
while(1)
{
read(lcd_fd,&xy,sizeof(xy));
if(xy.type == EV_ABS && xy.code == ABS_X ) //处理信息 {x=xy.value;}
if(xy.type == EV_ABS && xy.code == ABS_Y) {y=xy.value; }
if(xy.type == EV_KEY && xy.code == BTN_TOUCH && xy.value == 0)
{
if(y>315 &&y<360 && x>640 && x<800)//摄像头开关
{
if(video_open==0)//关闭摄像头
{
//...省略
}
else if(video_open==1)打开摄像头
{
//...省略
}
}
if(y>60 &&y<110 && x>640 && x<800)//月卡管理系统
{
//...省略
}
if(y>145 &&y<195 && x>640 && x<800)//相册:显示已入场车辆
{
//...省略
}
if(y>230 &&y<280 && x>640 && x<800)//显示备份视频
{
//...省略
}
}
}
}
pthread_join(tid,NULL);
return 0;
主要实现
创建线程,实时监测RFID模块状态,当有卡靠近时,读取卡号,先判断该卡号为临时卡还是月卡用户且过期与否,再判断该卡号车辆入场还是出场。根据不同的情况实现不同功能。具体过程如下图:
实体效果
月卡出场
临时卡出场
部分代码
while(1)
{
//...省略部分读取卡号过程
printf("该卡号为=%x\n",cardid);
//转换类型
char buf[24]="0";
sprintf(buf,"%x",cardid);
int a=seek_card(buf);//判断数据库是否已存在卡号
if(a == 0)//数据库中无改卡号,为入场
{
int month_tmp=decide_card(buf);//判断该卡是否为月卡
if(month_tmp == 1)//1为月卡
{
monthcard_day=decide_day(buf);//判断该月卡是否过期
printf("注册总天数:%d\n",monthcard_day);
if(monthcard_day<=30)//月卡未过期
{
char *month_chepai=get_chepai(buf,"month_card");//获取月卡车牌号
strcpy(chepai,month_chepai);
printf("该月卡的车牌号为:%s\n",month_chepai);
add_message(month_chepai,buf,"月卡");//信息添加进数据库
sprintf(sound,"车牌%s,月卡为%s已录入,入场成功",month_chepai,buf);
make_wav(sound);//语音播报
get_picture_flag = 1;//标志位,实现抓拍
}
else
{
make_wav("该月卡已经过期,请续费");
sleep(3);
month_tmp=0;
}
}
if(month_tmp==0)//0为临时卡
{
printf("请输入车牌号:\n");
make_wav("您使用的为临时卡,请输入车牌号");
scanf("%s",chepai);
add_message(chepai,buf,"临时卡");//入场信息添加进数据库
get_picture_flag = 1;//标志位,实现抓拍
}
}
else if(a==1)//数据库中已经存在该卡号,为出场
{
char show_card[16];//月卡或临时卡
int add_min=count_min(buf);//获取停车总时间
printf("停车时长为%d分钟\n",add_min);
int card_type=get_cardtype(buf);//查看数据库中卡的类型
if(card_type==1)//月卡计费每分钟0.2元
{
printf("月卡计费每分钟0.2元\n");
strcpy(show_card,"月卡");
money=0.2*add_min;//计算总费用
}
else//临时卡计费每分钟0.3元
{
printf("临时卡计费每分钟0.3元\n");
strcpy(show_card,"临时卡");
money=0.3*add_min;//计算总费用
}
printf("共需要缴纳金额为%0.2f元\n",money);
get_picture_card=1;//标志位 暂停监控,显示车辆出场信息
usleep(200000);
//===============================图片显示=============================
//printf("\n");
show_pohoto("chefei.bmp");//显示计费图片
char *out_chepai=get_chepai(buf,"my_table");//出场卡号的车牌号
printf("车牌为:%s\n",out_chepai);
sprintf(tmp,"/mnt/sd/protect3/tingchechang/%s.jpg",out_chepai);
lcd_draw_jpg(0,0,tmp);//显示车辆图片
//===============================字库显示=============================
show_date(out_chepai,640,45);//车牌号
show_money(money,660,180);//金额
show_time(add_min,660,100);//停车总时长
show_date(show_card,640,230);//卡类型
//===============================语音播报=============================
if(strcmp(show_card,"月卡")==0)
{
monthcard_day=decide_day(buf);//判断该月卡是否过期
monthcard_day=30-monthcard_day;
printf("月卡剩余天数:%d\n",monthcard_day);
sprintf(sound,"停车时长%d分钟,共%0.2f元,月卡剩余天数为%d天",add_min,money,monthcard_day);
make_wav(sound);
}
else
{
sprintf(sound,"停车时长%d分钟,共%0.2f元,为临时卡",add_min,money);
make_wav(sound);
}
//===============================删除数据=============================
delete_message(buf);//从数据库中删除该车辆信息
sprintf(delete_pic,"rm %s",tmp);//删除车牌图片
system(delete_pic);
}
}
else
{
printf("读卡失败\n");
make_wav("读卡失败");
}
sleep(2);
}
主要实现:(增删查改)
1.当用户想要登入月卡系统时,进入语音识别(程序设定好特定语句),只有用户念出指定口令才能登入系统,口令错误则无法登陆
2.登入系统后,程序从数据库月卡表格中读取并显示所有月卡用户信息(车牌号、卡号、用户名、注册时间)
3.增加用户:用户输入车牌号与注册时间,将信息录入月卡表格中
4.删除用户:输入用户名,删除月卡表格中该用户的所有信息
5.续费:输入用户名,若为月卡用户则继续输入续费时间,更新表格中该用户信息
6.修改用户资料:输入用户名,若为月卡用户则重新输入车牌号、卡号或用户名更新表格中该用户信息
具体效果和代码如下:
实体效果
部分代码
int monthcard_system()//月卡管理系统
{
printf("\n");
show_pohoto("system.bmp");
p=speech_recognition();//语音识别,返回字符串地址
printf("%s\n",p);
if(strcmp(p,"你打球真像蔡徐坤")!=0 )
{
make_wav("口令错误");
return 1;
}
make_wav("口令正确,成功登陆");
while(1)
{
all_card();//字库显示所有月卡的用户信息
while(1)
{
read(lcd_fd,&xy,sizeof(xy));
//处理信息
if(xy.type == EV_ABS && xy.code == ABS_X ){x=xy.value;}
if(xy.type == EV_ABS && xy.code == ABS_Y) {y=xy.value;}
if(xy.type == EV_KEY && xy.code == BTN_TOUCH && xy.value == 0)
{
printf("x=%d,y=%d\n",x,y);
if(y>60 &&y<110 && x>640 && x<800)//增加用户
{
show_system("\n\n请依次录入需要注册月卡用户的信息:\n (车牌 卡号 名字 注册月 注册日)",65,65,40);
printf("请依次录入需要注册月卡用户的信息:(车牌 卡号 名字 注册月 注册日)\n");
scanf("%s %s %s %s %s",car,card,name,month,day);
add_month_system(car,card,name,month,day);//将用户信息添加进数据库
break;
}
if(y>140 &&y<190 && x>640 && x<800)//删除用户
{
printf("请输入需要删除的用户名:\n");
show_system("\n\n--请输入需要删除的用户名--",65,65,55);
scanf("%s",tmp_name);
delete_month_system(tmp_name);//将用户信息从数据库中删除
break;
}
if(y>230 &&y<280 && x>640 && x<800)//续费
{
printf("请输入需要续费的用户名\n");
show_system("\n\n-请输入需要续费的用户名-",65,65,55);
scanf("%s",tmp_name);
int num=decide_name(tmp_name);//判断月卡数据库中是否存在该用户
if(num==1)//月卡系统中有该用户
{
printf("请分别输入续费的月、日\n");
show_system("\n\n-请分别输入续费的月、日-",65,65,55);
scanf("%s %s",month,day);
update_month_system(month,day,tmp_name);//续费
break;
}
else if(num==0)//月卡系统中无该用户,跳出重新注册
{
show_system("\n\n ---系统无该用户---",65,65,60);
sleep(2);
break;
}
}
if(y>315 &&y<365 && x>640 && x<800)//修改资料
{
printf("请输入需要修改资料的用户名\n");
scanf("%s",tmp_name);
int num=decide_name(tmp_name);//判断月卡数据库中是否存在该用户
if(num==1)//月卡系统中有该用户
{
show_system("\n\n请依次录入需要修改的用户信息:\n (车牌 卡号 名字 )",65,65,40);
printf("请依次录入需要修改的用户信息:(车牌 卡号 名字)\n");
scanf("%s %s %s",car,card,name);
update_system(car,card,name,tmp_name);//更新信息
break;
}
else if(num==0)//月卡系统中无该用户,跳出重新注册
{
show_system("\n\n ---系统无该用户---",65,65,60);
sleep(2);
break;
}
}
if(y>425 &&y<480 && x>725 && x<800)//返回监控
{
return 1;
}
}
}
}
}
主要实现
获取车辆入场时拍下的图片保存进节点,实现左右滑动查看入场车辆与相应车牌号。再与月卡系统中的数据对比,若入场车辆为月卡用户则显示出车主照片。
具体效果和代码如下:
实体效果
部分代码
int show_pic(struct double_list *head)//相册:显示已入场车辆
{
DIR *dir_p=opendir("/mnt/sd/protect3/tingchechang");//打开照片目录
struct dirent *msg;
while(msg=readdir(dir_p))//遍历目录内的文件
{
if( strstr(msg->d_name,".jpg")!=NULL)
{
inser_list(head,msg->d_name);//将目录中的视频名字依次保存在节点中
}
}
if(head->next==head)
{
make_wav("车场无车辆");
return 1;
}
char buf[24];//语音播报车库中有多少辆车
sprintf(buf,"停车场中有%d辆车",len);
make_wav(buf);//语音播报
show_pohoto("bili.bmp");//显示界面
struct double_list *tmp = head->next;
int xx,yy,xxx,yyy;
char tmp_buf[64];
sprintf(tmp_buf,"/mnt/sd/protect3/tingchechang/%s",tmp->pic_name);
lcd_draw_jpg(0,0,tmp_buf);//显示车辆图片
strcpy(tmp_buf,tmp->pic_name);
strtok(tmp_buf,".");
show_date(tmp_buf,640,50);//字库显示车牌
seek_chepai(tmp_buf);//判断显示车辆车主图片
while(1)
{
while(1)
{
read(lcd_fd,&xy,sizeof(xy));
//处理信息
if(xy.type == EV_ABS && xy.code == ABS_X ){x=xy.value;}
if(xy.type == EV_ABS && xy.code == ABS_Y) {y=xy.value;}
if(xy.type == EV_KEY && xy.code == BTN_TOUCH )
{
if( xy.value == 1)
{
xx = x;
yy = y;
}
else
{
if(y>420 &&y<480 && x>720 && x<800)//退出
{
delete_list(head);
return 1;
}
xxx=x-xx;
yyy=y-yy;
break;
}
}
}
if(yyy>=-30&&yyy<=30&&xxx>20) //向右滑动,下一张图片
{
tmp=tmp->next;
if(tmp==head)
{
tmp=tmp->next;
}
sprintf(tmp_buf,"/mnt/sd/protect3/tingchechang/%s",tmp->pic_name);
lcd_draw_jpg(0,0,tmp_buf);//显示车辆图片
strcpy(tmp_buf,tmp->pic_name);
strtok(tmp_buf,".");
show_date(tmp_buf,640,50);
seek_chepai(tmp_buf);//判断显示车辆车主图片
}
if(yyy>=-30&&yyy<=30&&xxx<-20) //向左滑动,上一张图片
{
tmp=tmp->prev;
if(tmp==head)
{
tmp=tmp->prev;
}
sprintf(tmp_buf,"/mnt/sd/protect3/tingchechang/%s",tmp->pic_name);
lcd_draw_jpg(0,0,tmp_buf);//显示车辆图片
strcpy(tmp_buf,tmp->pic_name);
strtok(tmp_buf,".");
show_date(tmp_buf,640,50);
seek_chepai(tmp_buf);//判断显示车辆车主图片
}
}
}
主要实现
通过VAL2摄像头截取每一帧图片,保存到目录中,当截取的图片数量达到500张时,利用ffmpeg指令将所有图片合成视频,当用户进入监控界面时,利用Mplayer指令播放该合成的视频,实现监控功能。
具体效果和代码如下:
实体效果
部分代码
录制视频
//=========================================录制视频====================================
//创建一个新的文件
bzero(picture, 20);
sprintf(picture, "%d.jpg",a);
fd = open(picture, O_RDWR | O_CREAT, 0777);
if(-1 == fd)
{
perror("create jpg failed");
continue;
}
//保存图片
write(fd, video_buf.jpg_data, video_buf.jpg_size);
close(fd);
sprintf(buf,"mv %s /mnt/sd/protect3/video_jpg",picture);//每一帧图片移动到指定文件夹
system(buf);
a++;
if(a==500)//当保存500张图片时,将所有图片合成视频
{
printf("视频合成中\n");
system("rm video.avi");//删除前一分钟录制的视频,防止系统提示是否删除,是程序能通畅执行
system("ffmpeg -f image2 -i /mnt/sd/protect3/video_jpg/%d.jpg video.avi");//ffmpeg合成视频指令
printf("视频合成完毕\n");
a=1;
}
显示视频
int show_video()//显示备份视频
{
pid_t pid;
pid =fork();
if(pid == 0) //子进程
{
printf("\n");
show_pohoto("shiping.bmp");
int fd = open("/pipe1",O_RDWR);//打开通信的管道文件
if(fd < 0)
{
perror("fail:");
exit(0); //退出进程
}
dup2(fd,1);//重定向标准输出设备描述符
//=======进行触摸屏操作========
pthread_t tid3;
pthread_create(&tid3,NULL,finger,NULL);
pthread_detach(tid3);
system("mplayer -slave -quiet -input file=/pipe -geometry 0:0 -zoom -x 750 -y 410 /mnt/sd/protect3/video.avi");
}
wait(NULL);
}
此次项目主要考验对SQLite数据库增删查改的使用,视频监控的录制,RFID读卡,摄像头截取图片,ffmpeg指令,字符串的精确提取,停车时间的计算…
在获取网络时间中,提取JOSN数据中指定数据花费了较多时间…
对出入场时间的计算也是一个较为复杂的过程(运用较多切割函数strtok)…
多次运用了提取数据库信息的函数…
在图片截取与视频录制合成过程中需要避免其他因素干扰…