目录
- 1 项目描述
- 2 项目需求
- 3 搭建环境
- 4 技术描述
- 5 概要设计
- 6 GIF表情切换
- 7 机器人智能回答
- 8 城市天气查询
- 9 在线播放音乐
- 10 播放网站视频
- 11 查看网页照片
- 12 项目总结
1 项目描述
本次项目是基于Linux环境的交叉编译arm-linux-gcc,在GEC6818arm开发板上运行,通过SOCKET编程,应用基于TCP/IP传输数据的HTTPS协议连接机器人API接口,根据用户输入的问题建立通信,返回数据,通过屏幕显示以及语音播报告知用户,实现聊天功能;通过语音识别或键盘输入选择功能:查询城市天气(API)、切换机器人动态表情(线程滚动)、看视频听音乐查看图片(Apache服务器在线查看)
2 项目需求
-
[1 ] 显示机器人表情(jpg/bmp图像显示)动态
-
[ 2] 实现基本的聊天功能(在键盘输入问题,就会在开发板的LCD屏幕上显示对应回答信息)HTTP请求
-
[ 3] 可以根据用户的输入查询不同城市的天气并显示到LCD设备上 JSON数据处理
-
[ 4] 可以浏览服务器上的图片文件(Apache) 利用HTTP协议下载文件
-
[5 ] 可以播放网络服务器上的视频文件 mplayer和http协议
-
[ 6] 自行设计一些新的功能提供用户使用
3 搭建环境
开发环境:
Linux
开放工具:
arm-linux-gcc、Notepad++
其他工具:
mplayer开源多媒体库、jpeglib库、font库、Apache服务器、讯飞语音、ALSA库
编程实现:
通过开源代码编译器notepad++编写代码,利用arm-linux-gcc交叉编译,再通过LINUX平台的SSH服务器将编译生成的程序文件传输到开发板中,最后执行。
Mplayer开源多媒体库
MPlayer是一款开源的多媒体播放器,以GNU通用公共许可证发布。此款软件可在各主流作业系统使用,例如Linux和其他类Unix作业系统、微软的视窗系统及苹果电脑的Mac OS X系统。
jpeglib库
libjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。这个库由独立JPEG工作组维护。
font库
通过封装font字库,让开发者能够自定义画板和文字颜色及大小。具体运用代码如下:
struct LcdDevice *init_lcd(const char *device)//初始化LCd
{//申请空间struct LcdDevice* lcd = malloc(sizeof(struct LcdDevice));if(lcd == NULL){return NULL;} lcd->fd = open(device, O_RDWR);//1打开设备if(lcd->fd < 0){perror("open lcd fail");free(lcd);return NULL;} lcd->mp = mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcd->fd,0);//映射 return lcd;
}int show_date(char*name)//字库显示字符串
{struct LcdDevice* lcd = init_lcd("/dev/fb0"); //初始化Lcd font *f = fontLoad("/usr/share/fonts/DroidSansFallback.ttf"); //打开字体 fontSetSize(f,40); //字体大小的设置bitmap *bm = createBitmapWithInit(400,230,4,getColor(0,0,0,0)); 创建一个画板(点阵图),改变画板颜色char buf[1024];strcpy(buf,name);fontPrint(f,bm,0,0,buf,getColor(0,201,174,255),400);//将字体写到点阵图上show_font_to_lcd(lcd->mp,190,135,bm);//把字体框输出到LCD屏幕上fontUnload(f);//关闭字体,关闭画板destroyBitmap(bm);
}
Apache阿帕奇服务器
Apache音译为阿帕奇, 是全世界最受欢迎的web服务器,因其快速、可靠并且可通过简单的API扩充,能将Python\Perl等解释器部署在其上面等优势,受到广泛的关注与使用。
此次项目就运用阿帕奇服务器搭建一个WEB服务器,使开发板可以直接连接到WEB服务器下载或显示文件。
讯飞语音
此次项目使用的是科大讯飞识别库(包括了语音合成和识别),开发板通过ALSA库的搭建能够录制语音,通过UDP协议传输到Linux平台,平台再通过识别语音生成文字返还给开发板,从而达到识别的功能。语音播报同样也是通过UDP协议将需要播报的字符串传送至Linux,通过语音合成音频文件,保存到WEB服务器的音频目录下,开发板通过在线播放改音频达到语音播报的功能。
具体项目实例如下:
ALSA库
ALSA是Advanced Linux Sound Architecture,高级Linux声音架构的简称,它在Linux操作系统上提供了音频和MIDI(Musical Instrument Digital Interface,音乐设备数字化接口)的支持。在2.6系列内核中,ALSA已经成为默认的声音子系统,用来替换2.4系列内核中的OSS(Open Sound System,开放声音系统)。
此次项目运用的主要是录音功能。
4 技术描述
基本
- C语言
- 文件IO
- 系统编程
- Mplayer指令
- 触摸屏
- font库的使用
- jpeglib库的使用
核心 - 链表 :通过线程,创建需要显示的GIF表情(每一帧生成一个动态表情)链表实现循环滚动,切换表情时清空节点再重新生成新的表情链表。
- TCP/IP协议、HTTP协议:运用SOCKET编程,通过基于TCP的http协议连接机器人和天气的API接口,再返回数据形成字符串实现项目功能。
- UDP协议:利用UDP协议实现语音播报和语音识别的功能。在开发板与Linux之间双向传输数据。
- JSON数据处理:将天气API接口返回的数据中,运用JSON数据的处理提取所需要的字符串。
5 概要设计
整体项目框架设计图如下:
6 GIF表情切换
实现思路:
1.当用户选择需要显示的GIF表情时,程序通过创建链表、遍历对应表情目录所有图片存进节点,再创建线程循环滚动图片(记得加延迟时间);
2.当需要切换表情或者选择其他功能的时候,程序会清空该表情的链表节点、结束线程,然后再重新存新的表情包进链表节点,重复步骤1…
具体效果和代码如下:
3.项目中添加了11种动态表情:wink、笑、哭、怒、喜欢、困、爱心、发呆、疑问、音乐
实体效果:
具体部分代码:
int main()
{head = malloc(sizeof(struct double_list)); //gif表情头节点head->next = head; head->prev = head; head->pic_name[200]=0;char http[1024];char word[1024];char buf[1024] = {0};printf("\n");show_pohoto("pingmu.bmp");while(1){printf("选择小白表情:wink、笑、哭、怒、喜欢、困、爱心、发呆、疑问 \n");scanf("%s",num);if(strcmp(num,"wink") == 0)//显示wink表情{ if(i==0){show_face(num,head);//显示表情i=1;}if(i==1){pthread_cancel(tid);//取消线程delete_list(head);//删除所有节点 show_face(num,head);//显示表情 } }else if(strcmp(num,"笑") == 0)//显示笑表情{ if(i==0){show_face(num,head);//显示表情i=1;}if(i==1){pthread_cancel(tid);//取消线程delete_list(head);//删除所有节点 show_face(num,head);//显示表情 } }}int show_face(char *face,struct double_list *head)//遍历表情目录存进链表,再用线程滚动链表图片
{char buff[64]="/robot/";strcat(buff,face);DIR *dir_p=opendir(buff);//打开图片目录struct dirent *msg; while(msg=readdir(dir_p))//遍历目录内的文件{if( strstr(msg->d_name,".JPG")!=NULL){inser_list(head,msg->d_name);//将目录中的视频名字依次保存在节点中} } show_list(head);usleep(400000); pthread_create(&tid,NULL,roll_wink,NULL);
}void *roll_wink(void *arg)//线程滚动链表图片{struct double_list *tmp=head; char buff[64];while(1){ tmp=tmp->next; if(tmp==head){tmp=tmp->next;}bzero(buff,sizeof(buff));sprintf(buff,"/robot/%s/%s",num,tmp->pic_name); show_jpg(buff,190,135);usleep(300000);}}
7 机器人智能回答
实现思路
通过SOCKET编程,使用HTTP协议连接机器人API接口,输入用户问题,客户端处理完返还数据,实现双向传输;将返回的数据经过字符串的一系列处理,通过字库显示至屏幕
具体效果和代码如下:
实体效果:
部分代码:
//使用IPV4的协议,基于TCPint new_socket = socket(AF_INET, SOCK_STREAM, 0);//设置需要链接到的服务器IP地址信息 例如:百度服务器 14.215.177.38 端口8080 struct sockaddr_in sockaddr_i; sockaddr_i.sin_family = AF_INET;sockaddr_i.sin_port = htons(80);sockaddr_i.sin_addr.s_addr = inet_addr("47.107.120.234"); //链接服务器int ret=connect(new_socket, (struct sockaddr *)&sockaddr_i, sizeof(sockaddr_i));sprintf(http, "GET /api.php?key=free&appid=0&msg=%s HTTP/1.1\r\nHost:api.qingyunke.com\r\n\r\n", num);write(new_socket,http,strlen(http));read(new_socket, buf, 1024);//-----------------------------字符串数据处理--------------------------------- char *handle=strstr(buf,"{");char *handle2=strstr(handle,"content");char *handle3=strstr(handle2,":");char xiaobai[1024]="小白";strcat(xiaobai,handle3); show_date(xiaobai);//显示小白的回答make_wav(handle3);i=xiaobai_face(xiaobai,head);//判断小白回答是否产生表情bzero(buf,1024);
8 城市天气查询
实现思路:
通过SOCKET编程,使用HTTP协议连接天气API接口,输入查询天气的城市,客户端处理完返还数据,实现双向传输;将返回的数据经过JOSN数据处理提取所需要的信息,再通过字库显示至屏幕。
具体效果和代码如下:
实体效果:
部分代码:
int answer_weather()//回答城市的天气
{while(1){ printf("请输入你要查询的城市:广州、深圳、惠州、肇庆、东莞、梅州、湛江、珠海 或 退出\n");scanf("%s",num);if(strcmp(num,"1") == 0)//语音识别{ speech_recognition(); }printf("date=%s\n",num);if(strcmp(num,"广州") == 0)//显示广州天气{char city_id[64]="101280101"; show_weather(city_id);}else if(strcmp(num,"深圳") == 0)//显示深圳天气{char city_id[64]="101280601"; show_weather(city_id);}//省略其他城市天气else if(strcmp(num,"退出") == 0)//退出{printf("\n");show_pohoto("pingmu.bmp"); show_jpg("待机.JPG",190,135);break;}else {printf("没有找到 %s 的天气\n",num);}}
}int show_weather(char *city_id)//显示城市天气情况
{//1.使用IPV4的协议,基于TCPint new_socket = socket(AF_INET, SOCK_STREAM, 0);//设置需要链接到的服务器IP地址信息 链接到自己的HTTP 服务器struct sockaddr_in sockaddr_i; sockaddr_i.sin_family = AF_INET;sockaddr_i.sin_port = htons(80);sockaddr_i.sin_addr.s_addr = inet_addr("58.222.18.2");//链接服务器int ret=connect(new_socket, (struct sockaddr *)&sockaddr_i, sizeof(sockaddr_i));char http[1024];sprintf(http,"GET /api/weather/city/%s HTTP/1.1\r\nHost:t.weather.itboy.net\r\n\r\n",city_id); write(new_socket,http,strlen(http));sleep(2);char buf[4096*2] = {0};read(new_socket, buf, 4096*2);char *handle=strstr(buf,"{");//把数据转换成 JSON 对象 cJSON *obj=cJSON_Parse(handle);char date[64];//日期char city[64];//城市char week[64];//星期char weather[64];//天气char low[64];//低温char high[64];//高温char notice[64];//建议cJSON *new_obj=cJSON_GetObjectItem(obj,"cityInfo");cJSON *value=cJSON_GetObjectItem(new_obj,"city"); strcpy(city,cJSON_Print(value));cJSON *obj_1=cJSON_GetObjectItem(obj,"data");cJSON *obj_arry=cJSON_GetObjectItem(obj_1,"forecast"); cJSON *obj_arry_0=cJSON_GetArrayItem(obj_arry,0); //日期value=cJSON_GetObjectItem(obj_arry_0,"ymd"); strcpy(date,cJSON_Print(value));//星期value=cJSON_GetObjectItem(obj_arry_0,"week"); strcpy(week,cJSON_Print(value));//天气value=cJSON_GetObjectItem(obj_arry_0,"type"); strcpy(weather,cJSON_Print(value));//低温value=cJSON_GetObjectItem(obj_arry_0,"low"); strcpy(low,cJSON_Print(value));//高温value=cJSON_GetObjectItem(obj_arry_0,"high"); strcpy(high,cJSON_Print(value));//建议value=cJSON_GetObjectItem(obj_arry_0,"notice"); strcpy(notice,cJSON_Print(value));char word_weather[256];sprintf(word_weather,"日期:%s\n 城市:%s\n 星期:%s\n 天气:%s\n %s %s\n %s\n",date,city,week,weather,low,high,notice);show_date(word_weather);//字库显示make_wav(word_weather);//语音播报}
9 在线播放音乐
实现思路:
通过system指令直接连接Apache服务器上音乐目录下的相应歌曲(实现在线播放,再通过管道实现歌曲调节功能);通过线程滚动显示“音乐”GIF表情以及字库显示歌曲名字。
具体效果和代码如下:
实体效果:
部分代码:
int interweb_music(char *num)//音乐功能菜单
{ char buff[128];char buf[64]; int music_fd = open("/robot/pipe2",O_RDWR);//打开通信的管道文件 sprintf(buff,"mplayer -slave -quiet -input file=/robot/pipe2 http://192.168.8.72/music/%s.mp3 &",num);//将音乐指令拼接到BUFF中 system(buff); usleep(400000);system("echo volume 10 1 > /robot/pipe2"); //降低初始音量music_name(num);//显示歌曲名字strcpy(num,"音乐");show_face(num,head);//显示音乐表情while(1){printf("请选择播放功能:1、暂停or播放 ; 2、放大音量 ; 3、降低音量 ; 4、退出播放\n");scanf("%s",buf);if(strcmp(buf,"1") == 0)//暂停or播放{ write(music_fd,"pause\n",strlen("pause\n"));}else if(strcmp(buf,"2") == 0)//放大音量{ write(music_fd,"volume +20\n",strlen("volume +20\n"));}else if(strcmp(buf,"3") == 0)//降低音量{ write(music_fd,"volume -20\n",strlen("volume -20\n"));}else if(strcmp(buf,"4") == 0)//停止播放{ write(music_fd,"quit\n",strlen("quit\n"));//停止播放 pthread_cancel(tid);delete_list(head); return 1; }}
}
10 播放网站视频
实现思路:
http协议连接Apache服务器下的相应视频,下载至开发板,再通过Mplayer播放视频,实现调节功能。
具体效果和代码如下:
实体效果:
部分代码:
int interweb_video(char *movie)//下载并播放视频
{int new_socket = socket(AF_INET, SOCK_STREAM, 0);//设置需要链接到的服务器IP地址信息 链接到自己的HTTP 服务器struct sockaddr_in sockaddr_i; sockaddr_i.sin_family = AF_INET;sockaddr_i.sin_port = htons(80);sockaddr_i.sin_addr.s_addr = inet_addr("192.168.8.72");//链接服务器int ret=connect(new_socket, (struct sockaddr *)&sockaddr_i, sizeof(sockaddr_i));char http[1024];sprintf(http,"GET /video/%s.avi HTTP/1.1\r\nHost:192.168.8.72\r\n\r\n",movie);write(new_socket,http,strlen(http));//创建一个本地文件 int fd=open("my.avi",O_RDWR|O_CREAT,0777);int flag=0; //接收服务器的回发数据 while(1){//读取服务器的数据char buf[4096]={0};int ret=read(new_socket,buf,4096);if(flag==0){char *tmp=strstr(buf,"\r\n\r\n");int len=tmp-buf+4;write(fd,buf+len,ret-len);flag=1;}elsewrite(fd,buf,ret);//写入到本地文件中}close(fd);pthread_cancel(tid3);usleep(100000);pid_t pid;pid =fork();if(pid == 0) //子进程{char buff[100];//接收拼接好的播放视频指令printf("\n");show_pohoto("shiping.bmp"); drw_sound(5); int fd = open("/robot/pipe1",O_RDWR);//打开通信的管道文件dup2(fd,1);//重定向标准输出设备描述符 //=======刷新进度条=======pthread_t tid;pthread_create(&tid,NULL,func,NULL);pthread_detach(tid); //=======进行触摸屏操作========pthread_t tid1;pthread_create(&tid1,NULL,finger,NULL);pthread_detach(tid1); //=============退出操作===========pthread_t tid2;pthread_create(&tid2,NULL,video_exit,NULL);system("mplayer -slave -quiet -input file=/robot/pipe -geometry 0:0 -zoom -x 750 -y 410 /robot/my.avi"); exit(0);} wait(NULL);
}
11 查看网页照片
实现思路:
http协议连接Apache服务器下的相应照片下载至开发板,再通过映射显示BMP图片。
具体效果和代码如下:
实体效果:
部分代码:
int down_file(char *file_name,char *http)//下载显示图片
{//使用IPV4的协议,基于TCPint new_socket = socket(AF_INET, SOCK_STREAM, 0); //设置需要链接到的服务器IP地址信息 例如:百度服务器 14.215.177.38 端口8080 struct sockaddr_in sockaddr_i; sockaddr_i.sin_family = AF_INET;sockaddr_i.sin_port = htons(80);sockaddr_i.sin_addr.s_addr = inet_addr("192.168.8.72");//链接服务器int ret=connect(new_socket, (struct sockaddr *)&sockaddr_i, sizeof(struct sockaddr_in));char host[64];char url[512];//字符串处理 char tmp_http[1024]={0};strcpy(tmp_http,http);strcpy(url,strstr(strstr(tmp_http,"//")+2,"/"));//去掉HTTP头 strcpy(host,strtok(strstr(tmp_http,"//"),"/"));//发送HTTP 请求char http_reques[1024]={0};sprintf(http_reques,"GET %s HTTP/1.1\r\nHost:%s\r\n\r\n",url,host);write(new_socket,http_reques,strlen(http_reques));//接收服务器返回的数据 char head[1024] = {0};ret=read(new_socket,head,1024);//printf("head=%s\n",head);int file_size=0;//获取文件大小sscanf(strstr(head,"Content-Length: "),"Content-Length: %d\r\n",&file_size);//printf("file_size=%d\n",file_size);//获取头数据的大小unsigned int head_len = (unsigned int)((strstr(head,"\r\n\r\n")+4)-head);//printf("%d\n",head_len);//写入头数据后的数据 int file_fd=open(file_name,O_RDWR|O_CREAT|O_TRUNC,0777);file_size-=ret-head_len;write(file_fd,(strstr(head,"\r\n\r\n")+4),(ret-head_len));char buf[4096]={0};while(1){bzero(buf,4096);ret=read(new_socket,buf,4096);write(file_fd,buf,ret); file_size-=ret; //计算读取的大小if(file_size == 0){printf("下载文件 %s 完毕\n",file_name);close(new_socket); //关闭通信break;}}show_jpg("my.jpg",0,0);//显示图片
}
12 项目总结
此次项目主要运用了网络编程中TCP/IP协议、UDP协议、HTTP协议、Apache服务器的使用、机器人和天气API接口以及科大讯飞语音识别、链表的使用、mplayer指令的选择、触摸屏的坐标处理、进/线程的创建与终止,对文件IO路径的认识。
主要难点在于机器人GIF表情的生成及切换(通过创建链表,节点存取,删除节点,线程滚动节点),天气JSON数据处理(需要对JSON数据处理有一定的了解,处理需要提取的字符串),语音识别和合成(利用UDP协议实现arm与语音识别、arm与语音合成)…