[Linux]基于SQLite数据库的停车管理系统-转千锋

文章目录
1 项目描述
2 项目需求
3 搭建环境
4 技术描述
5 概要设计
6 主界面
7 入场与出场
8 月卡系统
9 已入场车辆
10 监控
11 项目总结
1 项目描述
本次项目是基于Linux环境的交叉编译arm-linux-gcc,在GEC6818arm开发板上运行,通过摄像头监控,RFID模块识别卡号,判断该卡是否为月卡or临时卡,车辆入场or出场,记录或显示收费金额与车辆信息。运用SQLite数据库,创建表格存取月卡用户信息与出入车辆相关信息。增加语音播报功能,使项目更加人性化…

2 项目需求
(1)创建一个数据库, 内置你需要的所有信息, 如车牌信息、RFID卡信息、卡类、进场时间、车辆照片名等
(2) 在默认状态下,视频监控是处于打开状态的,并循环录制,1分钟视频,保存到本地。
(3)当检测到有RFID卡,关闭视频流,判断当前数据库中是否有该卡的入场信息:
如果已有该卡的入场信息,表示现在是出场;则直接计算出当前时间和入场时间差值,在屏幕上显示该车辆照片,显示车辆信息和应收费金额,停车总时间
如果没有该卡的入场信息,则在数据库中增加该车辆信息,并记录当前时间且拍照。
在检测到RFID卡3秒后 (alarm() SIGALRM信号), 系统会恢复打开视频流
(4)当车辆刷卡进场是,语音提示卡号,并说明卡的类别(临时卡,或包月卡),出场提示:费用与月卡剩余天数
(5)管理功能,负责把卡的类别进行修改,与数据库中的数据进行修改。
(6) 自行扩展: 添加一些自主功能。
(7)强测试代码,把BUG 去掉!!!!

3 搭建环境
开发环境:
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; 
1
2
3
4
删除

删除整个表格; 
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库、讯飞语音

4 技术描述
基本:

C语言
文件IO
系统编程
Mplayer指令
触摸屏
font库的使用
jpeglib库的使用
链表
JOSN数据处理
语音识别
ALSA库
核心

SQLite数据库
v4l2编程(摄像头信息采集)
ffmpeg库(视频合成)
RFID
5 概要设计
整体项目框架设计图如下:


6 主界面
主要实现:
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;

7 入场与出场
主要实现
创建线程,实时监测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
8 月卡系统
主要实现:(增删查改)
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;
                }
            }
        }
    }
}

9 已入场车辆
主要实现
获取车辆入场时拍下的图片保存进节点,实现左右滑动查看入场车辆与相应车牌号。再与月卡系统中的数据对比,若入场车辆为月卡用户则显示出车主照片。
具体效果和代码如下:
实体效果

部分代码

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);//判断显示车辆车主图片
            }        
        }
}

10 监控
主要实现
通过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);
    }

11 项目总结
此次项目主要考验对SQLite数据库增删查改的使用,视频监控的录制,RFID读卡,摄像头截取图片,ffmpeg指令,字符串的精确提取,停车时间的计算…
在获取网络时间中,提取JOSN数据中指定数据花费了较多时间…
对出入场时间的计算也是一个较为复杂的过程(运用较多切割函数strtok)…
多次运用了提取数据库信息的函数…
在图片截取与视频录制合成过程中需要避免其他因素干扰…
————————————————
版权声明:本文为CSDN博主「zhong丶senvi」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhong_senvi/article/details/96717902

你可能感兴趣的:(编程)