[Linux]基于网络编程的智能机器小伴侣

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、笑、哭、怒、喜欢、困、爱心、发呆、疑问、音乐
实体效果
GIF表情
具体部分代码:

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的协议,基于TCP
	int 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的协议,基于TCP
	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("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;
		}
		else
		write(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的协议,基于TCP
	int 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与语音合成)…

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值