基于语音识别的微博签到系统

作者:Catherine

语音识别与签到系统

近年来,语音识别在语音导航,室内设备控制,人际对话等方面得到了广泛的应用。

我们在今年第1期杂志《为设备添加社交网络功能》中,实现了W5500EVB自己发微博功能。试想如果我们把语音识别与微博签到结合起来,我们上班时,报上姓名,经识别后,摄像头为我们拍张照片,传到新浪微博,这样既能得到我们签到的时间,又能保证是本人签到,可靠高效,同时朋友通过微博能了解到我们上班时的状态,这样是不是很有意思呢?

今天要介绍的就是上面提到的,基于语音识别的微博签到系统,我们用摄像头ov2640拍照,LD3320做语音识别,然后W5500EVB把我们想说的话,以及照片发送到新浪微博。

基于语音识别的微博签到系统设计

(1)   开发板基本情况

a)        单片机:STM32F103RCT6,256K字节Flash,48K字节SRAM,2K字节EEPROM

b)       以太网控制器:W5500,SPI接口与单片机相连

c)        电源:USB供电

(2)   开发工具: IARfor ARM v5.41,这是我们工程所使用的版本。如果使用不同版本的IAR,请对STM的库稍作调整。

(3)   语音识别:LD3320语音识别模块。

(4)   图像生成:OV2640摄像头。

(5)   其他

a)        新浪微博用户名和密码;如若没有,就赶快给你的设备申请一个吧!

b)       一根Mini接口的USB线,如图1所示。

c)        一根网线。

d)       STM32芯片的串口程序烧录工具,STM官方提供的程序名为:Flash Loader Demo。

图1是系统实物图。

图1系统实物图

首先,我们了解一下整个程序流程,流程图由一个主流程图(见图2)和四个子流程图(图3,图4,图5,图6)组成。在STM32及ov2640初始化完成之后,将进行网络参数配置,根据自己网络的情况配置W5500的IP地址等网络参数,确保W5500能连接外网。然后配置LD3320语音模块,语音模块处于初始状态,将进行写入识别列表,启动语音识别过程,当我们对着麦克风说话的时候,LD3320检测到有语音输入,LD3320将进入中断,在中断中将把我们说的内容与寄存器里的词条比较,如果找到1-4个候选答案,返回“找到识别结果”状态,如果没有找到候选答案,返回“未找到识别结果”状态。在下一次循环中,LD3320如果是“找到识别结果”状态,将拍摄照片及发送微博,如果是“未找到识别结果”状态,将进入初始状态,如果是“正在识别”或者“识别错误”将重新检查LD3320的状态。各个子流程图描述的比较详尽,这里不再一一赘述。对于拍摄照片子流程图,我们需要了解jpg图片的数据格式,图片的前两个字节是0xff,0xd8,最后两个字节是0xff,0xd9,在中断程序接收图片数据的过程中,首先判断数据是不是前两个字节,如果是,保存数据,后面的数据是先保存,然后判断是不是数据结尾,直到接收成功。

图2系统主流程图

图3写入识别列表函数流程图                                                             图4启动语音识别模式流程图

图5拍摄照片流程图

图6发送微博流程图

以上四个子流程图,已清晰地给大家展示语音识别微博签到系统的整个工作流程,那么接下来就为大家揭开详细的制作过程。

LD3320介绍

1 通过快速而稳定的优化算法,完成非特定人语音识别,识别准确率95%。

2 不需要外接任何辅助的Flash芯片,RAM芯片和AD芯片,就可以完成语音识别功能。

3 每次识别最多可以设置50项候选识别句,每个识别句可以是单字,词组或短句,长度为不超过10个汉字或者79个字节的拼音串。识别句内容还可以动态编辑修改。

4 芯片内部已经准备了16位A/D转换器、16位D/A转换器和功放电路,麦克风、立体声耳机和单声道喇叭可以很方便地和芯片管脚连接。

5 支持并行和串行接口,串行方式可以简化与其他模块的连接。

在本系统中采用的LD3320模块如图7,LD3320芯片外部已经连接了麦克风,耳机接口,基本电路,只引出了我们需要的引脚。本系统采用串行方式,串行接口通过SPI协议和外部主CPU连接,首先要将MD接高电平,将SPIS接地,选定LD3320工作在串行模式,此时使用的管脚有:片选(SCS*)、SPI时钟(SDCK)、SPI输入(SDI)和SPI输出(SDO),中断引脚(INT),复位引脚(RST),时钟引脚(CLK),通过SPI接口,配置LD3320的工作模式,读取识别结果,图8,图9为SPI读写时序。当LD3320识别到有语音输入,INT引脚将产生中断,在中断处理函数中,读取识别结果,改变LD3320状态。

图7LD3320语音模块

图8  SPI方式读时序

图9 SPI方式写时序

在本系统中,OV2640输出JPEG压缩图像格式。MCU与OV2640的通信采用串行与并行结合,OV2640带有SCCB(Serial Camera Control Bus)双线串行接口,MCU通过SCCB接口配置和读取OV2640的信息;MCU通过并行总线的方式来接收OV2640的图像数据。Y(2..9)为8位MSB(MostSignificant Bit,最高有效位模式)并行总线,SDIO、SCLK为SCCB接口,PCLK为像素时钟输出管脚(每个周期从并行总线上输出一个像素),VSYNC为列同步输出管脚(每帧图像发生一次跳变),HERF为行参考输出管脚(每个周期总线从并行总线上输出一行图像数据)。系统的硬件电路连接简图如图10。

图10系统硬件电路连接简图

系统上电后,MCU配置OV2640的工作方式,初始化LD3320,然后检查LD3320的状态,当LD3320的状态是“找到识别结果”,开启OV2640中断,在OV2640准备好图像后,VSYNC会被拉高一段时间,MCU通过PCLK上升沿中断按字节接收图像数据,接收数据完成,关闭OV2640中断。然后向新浪微博发送已经写进程序里的自己想说的话和接收到的图片。接下来将对主要的程序块做介绍。

程序介绍

在《为你的设备添加社交网络功能》中,已经详细介绍了OV2640的初始化配置程序,本篇文章就不再赘述,图像数据缓存程序与本文稍有不同,这里简单介绍图像数据缓存程序。本文对LD3320的写入词条列表,启动语音识别,中断处理程序,发送微博程序做主要介绍。

图像数据缓存程序(摘至stm32f10x_it.c):

void EXTI0_IRQHandler(void)

{

u8 temp;

EXTI_ClearITPendingBit(EXTI_Line0);     //清除EXTI0线路挂起位

if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_1)==0)return;//HREF管脚为低

temp=(u8)((GPIOC->IDR)>>8 & 0x00ff);    //读取一个字节图像数据

switch(jpg_flag)

{

case 0:

if(temp==0xff)                      //图像数据以0xff 0xd8开头

{

JPEGBuffer[0]=0xff;

jpg_flag=1;

}

break;

case 1:

if(temp==0xd8)

{

JPEGBuffer[1]=0xd8;

jpg_flag=2;

JPEGCnt=2;

}

else if(temp!=0xff)

jpg_flag=0;

break;

case 2:

JPEGBuffer[JPEGCnt++] =temp;  //存储数据

if(temp==0xff)jpg_flag=3;

break;

case 3:

JPEGBuffer[JPEGCnt++]=temp;  //图像数据以0xff0xd9结尾

if(temp==0xd9)

{

jpg_flag=4;

}

else if(temp!=0xff)

jpg_flag=2;

break;

case 4:

break;

}

}

在中断函数中通过以上程序即可正确读取每一帧图像的数据了。程序思想已经在拍摄照片流程图中体现。JPEGBuffer为一个全局的图像缓存区,在主函数中,检测到缓存区数据准备完毕后,就可以将图像发送给新浪微博了。

LD3320添加词条程序(摘至LD3320_main.c)

uint8 LD_AsrAddFixed(void)

{

uint8 k, flag;

uint8nAsrAddLength;

#define DATE_A 4    /*数组二维数值*/

#define DATE_B  20     /*数组一维数值*/

uint8 sRecog[DATE_A][DATE_B] = {

"wenjuan",\

"guocui",\

"jierui",\

"chenge"\

};    /*添加关键词*/

uint8  pCode[DATE_A] = {

CODE_wenjuan,\

CODE_guocui,\

CODE_jierui,\

CODE_chenge\

};    /*添加识别码*/

flag = 1;

for (k=0; k<DATE_A; k++)

{

if(LD_Check_ASRBusyFlag_b2() == 0)

{

flag= 0;

break;

}

LD_WriteReg(0xc1, pCode[k] );

LD_WriteReg(0xc3, 0 );

LD_WriteReg(0x08, 0x04);

LD3320_delay(1);

LD_WriteReg(0x08, 0x00);

LD3320_delay(1);

for (nAsrAddLength=0; nAsrAddLength<DATE_B;nAsrAddLength++)

{

if (sRecog[k][nAsrAddLength] == 0)

break;

LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);

}

LD_WriteReg(0xb9, nAsrAddLength);

LD_WriteReg(0xb2, 0xff);

LD_WriteReg(0x37, 0x04);

LD_WriteReg(0x37, 0x04);

}

return flag;

}

列表的规则是,每个识别条目对应一个特定的编号(1个字节),不同的识别条目的编号可以相同,而且不用连续。本芯片最多支持50个识别条目,每个识别条目是标准普通话的汉语拼音(小写),每2个字(汉语拼音)之间用一个空格间隔。首先把识别条目的编号写入0xc1寄存器,其次,将字符串中的字符按顺序写入寄存器0x05,然后将字符串长度写入寄存器0xB9,向寄存器0xB2写入0xFF,向寄存器0x37写入0x04,通知DSP要添加一项识别句。

LD3320启动语音识别程序(摘至LD3320_main.c)

uint8 LD_AsrRun(void)

{

1     LD_WriteReg(0x35,MIC_VOL);   //ADC增益设置

2     LD_WriteReg(0x1C,0x09);      //ADC开关控制,写09H为保留命令字。

3     LD_WriteReg(0xBD,0x20);      //初始化控制寄存器,写入20H,保留命令字。

4   LD_WriteReg(0x08, 0x01);     //清除FIFO内容,第0位:写入1→清除FIFO_DATA。

LD3320_delay( 5);

5     LD_WriteReg(0x08,0x00);

LD3320_delay( 5);

6     if(LD_Check_ASRBusyFlag_b2()== 0)  //检查b2寄存器是否为空闲

{

return 0;

}

LD_WriteReg(0xB2,0xff);

7   LD_WriteReg(0x37, 0x06); //语音识别控制命令下发寄存器,写06H:通知DSP开始识别语音。

LD_WriteReg(0x37,0x06);

LD3320_delay( 5);

8  LD_WriteReg(0x1C, 0x0b); // ADC开关控制写0BH
麦克风输入ADC通道可用

LD_WriteReg(0x29, 0x10); // 中断允许(可读写)第4位:同步中断允许,1表示允许;0表示不允许。

LD_WriteReg(0xBD, 0x00);           // 初始化控制寄存器写入00H;然后启动;为ASR模块;

return 1;

}

第1行,ADC增益设置,或可以理解为麦克风(MIC)音量。可以设置为00H-7FH。建议设置值为40H-55H:值越大代表MIC音量越大,识别启动越敏感,但可能带来更多误识别;值越小代表MIC音量越小,需要近距离说话才能启动识别功能,好处是对远处的干扰语音没有反应。第6行检查LD3320是否为空闲状态,如果为空闲状态,在第7行向0x37寄存器写入0x06,通知DSP开始语音识别。第8行,向寄存器0x1c写入0x0b,表示麦克风输入ADC通道可用。

LD3320中断处理程序(摘至LDChip.c)

voidProcessInt0(void)

{

uint8nAsrResCount=0;

1  ucRegVal = LD_ReadReg(0x2B);  // 读取中断请求编号寄存器

2   LD_WriteReg(0x29,0);  // 第2位:FIFO 中断允许,1表示允许;0表示不允许。第4位:同步中断允许,1表示允许;0表示不允许。

3   LD_WriteReg(0x02,0);  // FIFO中断允许第0位:允许FIFO_DATA中断;第2位:允许FIFO_EXT中断;

4   If((ucRegVal& 0x10)&&LD_ReadReg(0xb2)==0x21 &&LD_ReadReg(0xbf)==0x35)        // 中断请求编号寄存器0x2B第4位:读取值为1表示语音识别有结果产生;MCU可清零。ASR过程中DSP忙闲状态寄存器0xb2,读取到0x21 表示闲,查询到为闲状态可以进行下一步ASR动作。ASR状态报告寄存器0xbf 读到数值为0x35,可以确定是一次语音识别流程正常结束.

             {   

5                  nAsrResCount= LD_ReadReg(0xba); //中断辅助信息寄存器,其中的数值表示语音识别有几个识别候选

6                  if(nAsrResCount>0&&nAsrResCount<=4)

               {

nAsrStatus=LD_ASR_FOUNDOK;                         

               }

else

               {

7nAsrStatus=LD_ASR_FOUNDZERO;

               }     

}

else

{

8  nAsrStatus=LD_ASR_FOUNDZERO;    //执行没有识别

}

LD_WriteReg(0x2b,0);

LD_WriteReg(0x1C,0);/*写0:ADC不可用*/

LD_WriteReg(0x29,0);

LD_WriteReg(0x02,0);

LD_WriteReg(0x2B,0);

LD_WriteReg(0xBA,0); 

LD_WriteReg(0xBC,0);  

LD_WriteReg(0x08,1);    /*清除FIFO_DATA*/

LD_WriteReg(0x08,0);    /*清除FIFO_DATA后再次写0*/

}

中断处理函数的第1行读取中断请求编号寄存器0x2B的值,第4位:读取值为1表示语音识别有结果产生;MCU可清零。第2位:读取值为1表示芯片内部FIFO中断发生。MP3播放时会产生中断标志请求外部MCU向FIFO_DATA中Reload数据。第3位:读取值为1表示芯片内部已经出现错误。值得注意的是:如果在中断响应时读到这位为1,需要对芯片进行重启Reset,才可以继续工作。第2,3行关闭LD3320的中断。第4行,读取中断请求编号寄存器0x2B的值,当第4位读取值为1表示语音识别有结果产生,其次读取语音识别过程中DSP忙闲状态寄存器0xb2,读取到0x21
表示闲,然后读取语音识别状态报告寄存器0xbf的值,读到数值为0x35,可以确定是一次语音识别流程正常结束,当这三个寄存器的数值不满足以上要求的时候,返回“LD_ASR_FOUNDZERO”,表示未找到识别结果。当满足以上要求时,第5行,读取中断辅助信息寄存器,其中的数值表示语音识别有几个识别候选,当数值为
1
– 4: 表示有N个识别候选,数值为0或者大于4表示没有识别候选,当有识别候选的时候,返回“LD_ASR_FOUNDOK,表示找到语音识别结果。

发送微博程序(摘至weibo.c)

unsigned char post_weibo_upload(char* weibo, uint8* pic,uint32 picLen)

{

unsigned char ret=0;

unsignedintlen=0;

1  if(socket(SOCK_WEIBO,Sn_MR_TCP,any_local_port++,0)!=1)  //to initialize a TCP socket

{

printf("Socket initialization failed.\r\n");

return 0;

}

else

{

printf("Connect with Weibo server.\r\n");

2   ret=connect(SOCK_WEIBO,weibo_server_ip,80);       //connect to the weibo server, default TCPport is 80

if(ret!=1)

{

printf("Connect Weibo server failed.\r\n");

return 0;

}

else

{

3while(getSn_SR(SOCK_WEIBO)!=SOCK_ESTABLISHED);  //wait for the TCP connection established!

printf("Connected with Weiboserver.\r\n");

4   sprintf(post_data,"\r\n--%s\r\nContent-Disposition:form-data; name=\"id\"\r\n\r\n%s"\

"\r\n--%s\r\nContent-Disposition:form-data; name=\"pw\"\r\n\r\n%s"\

"\r\n--%s\r\nContent-Disposition:form-data; name=\"cmd\"\r\n\r\nupload"\

"\r\n--%s\r\nContent-Disposition:form-data; name=\"status\"\r\n\r\n%s"\

"\r\n--%s\r\nContent-Disposition:form-data; name=\"file\"; filename=\"pic.jpg\"\r\nContent-Type:application/octet-stream\r\n\r\n",(char*)BOUNDARY,(char*)WEIBO_ID,(char*)BOUNDARY,(char*)WEIBO_PWD,(char*)BOUNDARY,(char*)BOUNDARY,weibo,(char*)BOUNDARY);//"\r\n--%s--\r\n"

5   sprintf(tmp_buf,"POST %sHTTP/1.1\r\nHost: %s\r\nUser-Agent: w5500\r\nContent-Type: multipart/form-data;boundary=%s\r\nConnection:close\r\nContent-Length:%d\r\n\r\n%s",(char*)HTTP_PATH,(char*)WEIBO_SERVER,(char*)BOUNDARY,strlen(post_data)+picLen+strlen((char*)BOUNDARY)+8,post_data);

6   len=send(SOCK_WEIBO,(unsignedchar*)tmp_buf,strlen(tmp_buf)); //upload your weibo content

uint16 file_len=picLen;

uint16 send_len=0;

while(file_len)

{

if(file_len>PACKET_LEN)

{

if(getSn_SR(SOCK_WEIBO)!=SOCK_ESTABLISHED)

{

return 0;

}

7send(SOCK_WEIBO, (uint8*)(pic+send_len), PACKET_LEN);  // upload picture

send_len+=PACKET_LEN;

file_len-=PACKET_LEN;

}

else

{

8send(SOCK_WEIBO, (uint8*)(pic+send_len), file_len);// uploadpicture

send_len+=file_len;

file_len-=file_len;

}

}

sprintf(tmp_buf,"\r\n--%s--\r\n",(char*)BOUNDARY);

send(SOCK_WEIBO,(unsigned char*)tmp_buf,strlen(tmp_buf));

while(1)

{

9len=getSn_RX_RSR(SOCK_WEIBO);

if(len>0)

{

memset(tmp_buf,0x00,MAX_BUF_SIZE);

10len=recv(SOCK_WEIBO,(unsigned char*)tmp_buf, len);  //receive thereturn result from weibo server

11char*p=strstr(tmp_buf,(char*)"\r\n\r\n")+4;   //gethttp payload without http headerprintf("%s\r\n",p);

disconnect(SOCK_WEIBO);  //disconnect with weibo server

close(SOCK_WEIBO);       //close the socket

return 1;                //sucess! return 1

}

}

}

}

}

发送微博函数的第1行,初始化一个socket,第2行,对服务器发出连接请求,第3行一直等待连接的建立。与服务器建立连接后,第4,5行负责组建带有微博内容和图片长度的HTTP数据包,第6行负责发送微博内容,第7,8行发送图片数据。第9行是读取W5500接收到的数据长度,第10行从W5500的接收缓存中把接收到的数据读到tmp_buf中。由于接收到的数据包含了HTTP头,第11行是把HTTP头去掉,得到服务器的返回结果。服务器返回结果的类型请参看《为你的设备添加社交网络功能》一文。

好了,代码就这么多,赶快编译烧到单片机里面吧,上电,对着麦克风说出一句已经写到LD3320里的话,当对应的指示灯亮或者闪烁,说明已经识别成功,然后对着摄像头微笑吧,这时摄像头为我们拍张照片,上传微博,然后看串口调试信息,如果收到“255:ok”,那就成功了,登录到微博看看,写进程序里的话以及自己的照片出现在微博上面。如图11。

图11系统发送微博效果图

至此,我们的基于语音识别的微博签到系统已经大功告成,你心动了吗?赶快制作你自己的微博签到系统吧。

欢迎与我们更多交流:[email protected]

WIZnet官方网站: http://www.iwiznet.cn

WIZnet官方微博:http://weibo.com/wiznet2012

时间: 2024-10-10 09:29:44

基于语音识别的微博签到系统的相关文章

基于Cobbler实现多版本系统批量部署

前言 运维自动化在生产环境中占据着举足轻重的地位,尤其是面对几百台,几千台甚至几万台的服务器时,仅仅是安装操作系统,如果不通过自动化来完成,根本是不可想象的.记得前面我们探究了基于PXE实现系统全自动安装,但PXE同时只能提供单一操作系统的批量部署,面对生产环境中不同服务器的需求,该如何实现批量部署多版本的操作系统呢?Cobbler便可以的满足这一实际需求,本文带来的是基于Cobbler实现多版本操作系统批量部署. Cobbler 简介 Cobbler是一款自动化操作系统部署的实现工具,由Pyt

RDIFramework.NET — 基于.NET的快速信息化系统开发框架 — 系列目录

RDIFramework.NET - 基于.NET的快速信息化系统开发框架 - 系列目录 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案.  框架简单介绍 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,为企业或个人在.NET环境下快速开发系统提供了强大的支持,开发人员不需要开发系统的基础功能和公共模块,框架自身提供了强大的函数库和开发包,开发人员只须集中精力专注于业务部分的开发,因此大大提高开

基于cobbler实现自动化安装系统

基于cobbler实现自动化安装系统 环境介绍 centos6.8 为centos6.8提供两块网卡 (非必要) 一块为桥接,方便xshell连接和测试 一块为vmnet3:用来搭建dhcp,tftp,和为客户端提供cobbler服务 前提,(确保安装TFTP,dhcp,rsync) # yum install httpd cobbler cobbler-web pykickstart debmirror 1.启动对应的服务 # service httpd start # service cob

Android弹幕实现:基于B站弹幕开源系统(4)-重构

?? Android弹幕实现:基于B站弹幕开源系统(4)-重构 弹幕在视频播放的APP中比较常见,但是逻辑比较复杂,现在在附录1,2,3的基础上,我再次对弹幕进行抽象和重构,把弹幕从底向上抽象成不同的层,便于复用. 第一步,抽象数据层.通常弹幕的来源是来源于后台的数据接口请求,在实时直播时候,是通过网络的轮询机制获取数据,那么,我把这部分代码抽出来设计成一个MGDanmakuHttpController,该类专注于数据的获取与分发: package zhangphil.danmaku; impo

Android弹幕实现:基于B站弹幕开源系统(1)

?? Android弹幕实现:基于B站弹幕开源系统(1) 如今的视频播放,流行在视频上飘弹幕.这里面做的相对比较成熟.稳定.使用量较多的弹幕系统,当推B站的弹幕系统,B站的弹幕系统已经作为开源项目在github上,其项目地址:https://github.com/Bilibili/DanmakuFlameMaster 以B站开源的弹幕项目为基础,现给出一个简单的例子,实现发送简单的文本弹幕.第一步,首先要在Android的build.gradle文件中引入B站的项目: repositories

基于Jenkins的自动构建系统开发_android总结

持续集成相关理论 1.1 极限编程的概述 1.1.1 极限编程的产生 2001年,为了解决许多公司的软件团队陷入不断增长的过程泥潭,一批业界专家一起概括出了一些可以让软件开发团队具有快速工作.响应变化能力的价值观和原则,他们称自己为敏捷联盟.敏捷开发过程的方法很多,主要有:SCRUM,Crystal,特征驱动软件开发(Feature Driven Development,简称FDD),自适应软件开发(Adaptive Software Development,简称ASD),以及最重要的极限编程(

Android弹幕实现:基于B站弹幕开源系统(2)

?? Android弹幕实现:基于B站弹幕开源系统(2) 在附录1的基础上,模拟实现一种实际开发的应用场景:从网络中不间断的周期取弹幕数据,这些弹幕数据往往是批量的,然后把这些从网络中取到的批量数据逐个的显示出来.注意本例中的Handler和线程安全队列ConcurrentLinkedQueue的使用.Java代码: package zhangphil.danmaku; import android.app.Activity; import android.graphics.Color; imp

基于jsp的新闻发布系统

新闻发布系统 下面就让我们来说一说基于jsp的新闻发布系统,其中使用的技术有JavaBean.fillter.数据库等,能够实现新闻的发布功能,在发布之后能够进行对每一条新闻的删除.修改.或者继续增加新的文章,最后还能够进行查询功能,其中引用了百度编辑器,能够进行图文并茂的编辑,极大地方便用户的使用. 注:完整项目下载地址:新闻发布系统 一.效果演示 首先让我们来看一看实现的效果: 下面是登陆的首界面: 图1 首界面 管理员登录页面: 图2 管理员登录界面 下面是管理员登陆之后的界面,可以进行添

【Zigbee技术入门教程-号外】基于Z-Stack协议栈的抢答系统

[Zigbee技术入门教程-号外]基于Z-Stack协议栈的抢答系统 广东职业技术学院  欧浩源 一.引言    2017年全国职业院校技能大赛"物联网技术应用"赛项中任务三题2的"抢答器开发"是一个非常优秀的题目.本人对题目进行了适当的改造和完善,剔除了和技能大赛中特别设备高度相关的内容,使其成为一个通用的Zigbee无线组网技术应用入门学习的综合实训案例.不管是自我学习,还是专业教学,甚至是在技能大赛训练中,这个题目都适合使用. 二.硬件资源需求    要进行本