查看: 840|回复: 0

【美好生活好搭拍客】专注力提升神器

[复制链接]

6

主题

12

帖子

123

积分

注册会员

Rank: 2

积分
123
发表于 2020-6-22 22:02:45 | 显示全部楼层 |阅读模式
本帖最后由 ngxxczlwh 于 2020-6-22 23:11 编辑

【设计背景】 低学龄孩子写作业是令许多家长头疼的事。孩子们写一会儿作业就喊着要吃东西、喝水或是上厕所,不能高效安静地完成作业,家长们看着只能干着急。孩子小,注意力不集中是其中的原因之一,孩子缺少这方面的训练,也是孩子不能专注做事的主要原因之一。怎样训练孩子在一段时间内集中注意力,专注于写作业呢?
思考良久,我就想创作一个可以用来培养孩子的专注力的作品,陪伴并训练孩子在一段时间内专心地写作业。

【功能描述】
1.在孩子准备写作业前,提醒孩子做好准备。
2.孩子写作业过程中,若有交互需求,给3次机会和孩子交流,并温馨提醒孩子专心写作业。
3.倒计时。默认给孩子30分钟时间专心写作业,倒计时时间到了,询问作业写好了没有,若孩子作业写完了,给与孩子激励和表扬,鼓励孩子的进步;若孩子作业没有完成,也要用激励性的语言给孩子加油,同时提醒孩子休息10分钟,为下一段学习时间做好准备。倒计时功能应该是独立的,其他的生活场景也可以随时使用倒计时功能,比如:洗澡、阅读、看电视、玩游戏等等。
4.其他简单的语音交互功能。
5.简单娱乐功能,如:语音点播歌曲。

【视频演示】



【物料清单】
1.Nano主控板及扩展板
Nano及扩展板.jpg
因为本作品中使用了语音识别等开源硬件,因此在主控板上选择了arduino平台的Nano板子作为主控板,小巧又便宜,适合编程造物。为了方便使用和调试,还选配了一块扩展板。
2.Ds1302时钟模块
ds1302.jpg
这是一款非常实用的时钟模块,为系统提供准确的时间信息,断电不掉时。
3.TM1650 4位数码管(带冒号)
TM1650正面.jpg

TM1650.jpg
TM1650 4位数码管,IIC总线驱动。
4.语音识别模块
asr.jpg
这是一款在淘宝上买的语音识别模块,价格有些小贵,但本系统设计的目标需要语音交互,少不了语音识别模块。
它也是iic总线驱动的,这样可以节省系统的引脚。
5.MP3语音模块及TF卡
MP32.jpg
MP3语音模块,用于语音交互,播放语音。TF卡用于存储语音交互时需要播放的MP3文件。
6.简易功放模块
功放.jpg
简易功放,用于将MP3模块解码的声音文件放大输出到喇叭。
7.小喇叭
喇叭.jpg
【电路连接】
接线图.jpg


【程序编写】
1.主程序部分
  1. #include <TM1650.h>             //TM1650数码管
  2. #include <Wire.h>                 //I2C语音识别模块
  3. #include <ThreeWire.h>          //DS1302时钟模块所用头文件
  4. #include <RtcDS1302.h>          //DS1302时钟模块头文件
  5. #include <mp3.h>                 //MP3音频模块头文件
  6. #include <ASR.h>                 //语音识别模块头文件

  7. TM1650 dis_1650;             //数码管实例化
  8. bool dotflag=false;           //控制数码管中间冒号
  9. bool isDaojishi=false;         //记录是否在倒计时中
  10. bool isWorking=false;         //记录是否在学习中
  11. uint8_t miao='0';             //监控秒数的变化,以显示冒号
  12. unsigned alarm_hour;          //倒计时时刻的时分秒
  13. unsigned alarm_minute;        //倒计时时刻的分
  14. unsigned alarm_second;        //倒计时时刻的秒
  15. //时钟对象实例化
  16. ThreeWire myWire(10,11,9);              // IO, SCLK, CE(DS1302时钟模块的三个引脚)
  17. RtcDS1302<ThreeWire> Rtc(myWire);     
  18. RtcDateTime now;
  19. //MP3对象实例化
  20. const int dq = A1;
  21. MP3 mp3(dq);
  22. //ASR语音识别对象实例化
  23. ASR myAsr;

  24. //系统初始化部分
  25. void setup() {

  26.     Wire.begin();     //Join the bus as master
  27.     myAsr.init();     //修改词条请在ASR.CPP文件中修改此方法;
  28.     dis_1650.init();  //TM1650数码管初始化部分
  29.     delay(1000);
  30.     //初始化mp3模块,设置音量
  31.     mp3.Init();       //MP3音频模块初始化
  32.     delay(200);
  33.     mp3.Volume_Set(15); //设置MP3音频模块音量,最大值30
  34.     //初始化随机种子
  35.     randomSeed(analogRead(0));
  36.    //初始化时钟模块
  37.     Rtc.Begin();
  38.    
  39.     #if 0
  40.       //设置的时间是可以掉电保存的,第一次设置完成后,可以将此段注释掉,即将1改为0,然后重新下载一次程序
  41.       RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
  42.       Rtc.SetDateTime(compiled);
  43.       //添加的词条和识别模式是可以掉电保存的,第一次设置完成后,可以将此段注释掉,即将1改为0,然后重新下载一次程序
  44.       myAsr.AddWords();
  45.     #endif
  46. }

  47. //倒计时函数,minute为倒计时的分钟数
  48. void daoJiShi(unsigned minute){   
  49.   isDaojishi=true;
  50.   RtcDateTime now = Rtc.GetDateTime();
  51.   alarm_second=now.Second();
  52.   alarm_minute=(now.Minute()+minute)%60;
  53.   alarm_hour=((now.Minute()+minute)/60+now.Hour())%24;
  54. }


  55. //displayTime为在数码管显示时间的函数
  56. #define countof(a) (sizeof(a) / sizeof(a[0]))
  57. void displayTime()
  58. {
  59. //    RtcDateTime now = Rtc.GetDateTime();
  60.     now =Rtc.GetDateTime();
  61.     char datestring[5];
  62.     snprintf_P(datestring,
  63.             countof(datestring),
  64.             PSTR("%02u%02u"),//%04u %02u:%02u:%02u"),
  65.             //dt.Month(),
  66.             //dt.Day(),
  67.             //dt.Year(),
  68.             now.Hour(),
  69.             now.Minute()
  70.             //dt.Second()
  71.             );
  72.     //Serial.print(datestring);
  73.     dis_1650.displayString(datestring);
  74.     if(miao!=now.Second()) {
  75.         dotflag=!dotflag;
  76.         miao=now.Second();
  77.     }
  78.     dis_1650.setDot(1,dotflag);
  79.     //Serial.println("displayTime is working");
  80. }

  81. //判断倒计时时间是否到了
  82. bool isTime()
  83. {
  84.     //Serial.println("isTime is working.");
  85.       if((now.Second()==alarm_second) and (now.Minute()==alarm_minute) and (now.Hour() ==alarm_hour))
  86.     {  
  87.       mp3.Song_Select(11);    //倒计时时间到了,播放提示音:时间到
  88.       isDaojishi=false;
  89.       return true;
  90.     }
  91.     else
  92.       return false;
  93. }

  94. //我要(想)写作业函数,完成写作业的整个交互流程
  95. void iWanttoWork()
  96. {
  97.   Serial.println("iWanttoWork is working.");
  98.   isWorking=true;                 //记录状态:要开始写作业了
  99.   mp3.Song_Select(9);             //询问是否需要花5分钟时间准备一下
  100.   unsigned char result;
  101.   result=myAsr.Listening();       //监听语音,并将语音识别的结果赋给result
  102.   while (result!=11)              //回答为“不需要准备”对应11
  103.   {
  104.     if (result==12)                 //回复:需要准备一下,给5分钟时间准备。
  105.       {
  106.         mp3.Song_Select(10);
  107.         daoJiShi(5);                //5分钟准备时间倒计时
  108.         while(!isTime())            //5分钟倒计时期间停在此处:显示时间、监听语音
  109.           {
  110.             displayTime();          //数码管显示时间
  111.             result=myAsr.Listening();
  112.             if (result==14){         //准备好了,跳出循环
  113.               mp3.Song_Select(16);    //准备好了的应答
  114.               break;
  115.             }
  116.           }
  117.          break;
  118.       }
  119.      result=myAsr.Listening();    //监听语音,并将语音识别的结果赋给result
  120.      displayTime();
  121.   }
  122.   mp3.Song_Select(12);            //播报:开始写作业
  123.   daoJiShi(30);                   //写作业倒计时开始
  124.   unsigned char countSpeak=0;     //记录写作业过程中讲话的次数,只回应前3次
  125.   while (!isTime())               //写作业的倒计时30分钟期间,程序停在此循环,完成:显示时间、监听语音并做相应的应答
  126.   {
  127.     displayTime();
  128.     result=myAsr.Listening();
  129.     switch(result)
  130.     {
  131.       case 2:
  132.       case 3:
  133.       case 7:
  134.       case 8: if(countSpeak<=3){countSpeak++;mp3.Song_Select(random(4,6));}break;         //回应3次(呼叫、听歌、吃东西、喝水)
  135.       case 13:mp3.Song_Select(15);return;                                                 //作业写完了,提前退出本函数。
  136.     }
  137.   }
  138.   mp3.Song_Select(13);//询问作业是否完成
  139.   while (true)        //确认作业是否完成
  140.   {
  141.     displayTime();
  142.     result=myAsr.Listening();
  143.     if(result==13)          //完成作业的应答
  144.       {mp3.Song_Select(15);break;}
  145.     if(result==21)          //作业未完成的应答
  146.       {mp3.Song_Select(14);break;}
  147.   }
  148. }

  149. //主循环程序
  150. void loop() {
  151.     //显示时间
  152.     displayTime();
  153.     //判断倒计时时间是否到了
  154.     isTime();
  155. //    if((now.Second()==alarm_second) and (now.Minute()==alarm_minute) and (now.Hour() ==alarm_hour))
  156. //    {  
  157. //      mp3.Song_Select(11);
  158. //      isDaojishi=false;
  159. //    }
  160. ///////////////////////////////////////////////////开始语音识别
  161.   unsigned char result;
  162.   unsigned char indexMp3;
  163.   result=myAsr.Listening();   //监听语音,并将语音识别结果赋给result
  164.   //Serial.println(result);
  165. switch(result){
  166.   case 2:mp3.Song_Select(random(1,4));break;                                  //呼叫鸡飞飞的应答
  167.   case 1:
  168.   case 5:
  169.   case 10:
  170.   case 15:
  171.   case 20:
  172.   case 30: if(!isDaojishi) {mp3.Song_Select(8);daoJiShi(result);}break;       //倒计时的处理
  173.   case 9: mp3.Song_Select(random(6,8));break;                                 //我回来了
  174.   case 4: if(!isWorking) iWanttoWork();break;                                 //我要写作业
  175.   case 3: indexMp3=random(30,46);mp3.Song_Select(indexMp3);break;             //我要听音乐的处理:随机播放30-46区间的MP3文件
  176.   case 7: mp3.Song_Select(17);break;                     //我要吃东西
  177.   case 16:if (indexMp3>30) mp3.Pre();break;              //上一首
  178.   case 17: if(indexMp3<45) mp3.Next();break;             //下一首
  179.   case 18: mp3.Stop();break;                             //停止播放音乐
  180.   //default:mp3.Song_Select(18);Serial.println("what are you saying?");  //其他情况
  181. }

  182. }
复制代码

2.语音识别部分

2.1语音识别模块头文件(ASR.h)
  1. #ifndef  _ASR_H_
  2. #define _ASR_H_

  3. #include <Wire.h>

  4. #define I2C_ADDR                0x79

  5. #define ASR_RESULT_ADDR           100
  6. //识别结果存放处,通过不断读取此地址的值判断是否识别到语音,不同的值对应不同的语音,
  7. #define ASR_WORDS_ERASE_ADDR      101//擦除所有词条
  8. #define ASR_MODE_ADDR             102
  9. //识别模式设置,值范围1~3
  10. //1:循环识别模式。状态灯常亮(默认模式)   
  11. //2:口令模式,以第一个词条为口令。状态灯常灭,当识别到口令词时常亮,等待识别到新的语音,并且读取识别结果后即灭掉
  12. //3:按键模式,按下开始识别,不按不识别。支持掉电保存。状态灯随按键按下而亮起,不按不亮
  13. #define ASR_ADD_WORDS_ADDR        160//词条添加的地址,支持掉电保存

  14. class ASR{
  15.         public:
  16.                 ASR(uint8_t asr_mode=1);
  17.                 void init();
  18.                 bool WireWriteByte(uint8_t val);
  19.                 bool WireWriteDataArray(  uint8_t reg,uint8_t *val,unsigned int len);
  20.                 int WireReadDataArray(   uint8_t reg, uint8_t *val, unsigned int len);
  21.                 bool ASRAddWords(unsigned char idNum,unsigned char *words);
  22.                 void AddWords();     //自定义词条的函数
  23.                 unsigned char Listening();   //监听语音
  24.         private:
  25.                 uint8_t ASRMode;
  26. };

  27. #endif        /*end _ASR_H_    */
复制代码

2.2 语音识别模块函数实现部分
  1. #include<ASR.h>
  2. #include<Arduino.h>

  3. ASR::ASR(uint8_t asr_mode)
  4. {
  5.         ASRMode=asr_mode;
  6. }

  7. bool ASR::WireWriteByte(uint8_t val)
  8. {
  9.     Wire.beginTransmission(I2C_ADDR);
  10.     Wire.write(val);
  11.     if( Wire.endTransmission() != 0 ) {
  12.         return false;
  13.     }
  14.     return true;
  15. }

  16. bool ASR::WireWriteDataArray(  uint8_t reg,uint8_t *val,unsigned int len)
  17. {
  18.     unsigned int i;

  19.     Wire.beginTransmission(I2C_ADDR);
  20.     Wire.write(reg);
  21.     for(i = 0; i < len; i++) {
  22.         Wire.write(val[i]);
  23.     }
  24.     if( Wire.endTransmission() != 0 ) {
  25.         return false;
  26.     }
  27.     return true;
  28. }

  29. int ASR::WireReadDataArray(   uint8_t reg, uint8_t *val, unsigned int len)
  30. {
  31.     unsigned char i = 0;  
  32.     /* Indicate which register we want to read from */
  33.     if (!WireWriteByte(reg)) {
  34.         return -1;
  35.     }
  36.     Wire.requestFrom(I2C_ADDR, len);
  37.     while (Wire.available()) {
  38.         if (i >= len) {
  39.             return -1;
  40.         }
  41.         val[i] = Wire.read();
  42.         i++;
  43.     }
  44.     /* Read block data */   
  45.     return i;
  46. }

  47. /*
  48. * 添加词条函数,
  49. * idNum:词条对应的识别号,1~255随意设置。识别到该号码对应的词条语音时,
  50. *        会将识别号存放到ASR_RESULT_ADDR处,等待主机读取,读取后清0
  51. * words:要识别汉字词条的拼音,汉字之间用空格隔开
  52. * 执行该函数,词条是自动往后排队添加的。
  53. */
  54. bool ASR::ASRAddWords(unsigned char idNum,unsigned char *words)
  55. {

  56.   Wire.beginTransmission(I2C_ADDR);
  57.         Wire.write(ASR_ADD_WORDS_ADDR);
  58.         Wire.write(idNum);
  59.         Wire.write(words,strlen(words));
  60.   if( Wire.endTransmission() != 0 ) {
  61.         delay(10);
  62.         return false;
  63.     }
  64.     delay(10);
  65.     return true;
  66. }

  67. void ASR::init()
  68. {
  69.   uint8_t ASRMode = 1;//1:循环识别模式    2:口令模式,以第一个词条为口令    3按键模式,按下开始识别
  70.   Wire.begin();
  71.   Serial.begin(9600);
  72.   if(WireWriteDataArray(ASR_MODE_ADDR,&ASRMode,1))
  73.     Serial.println("ASR Module Initialization complete");
  74.   else
  75.     Serial.println("ASR Module Initialization fail");


  76.   Serial.println("Start");
  77. }

  78. void ASR::AddWords()
  79. {
  80.   uint8_t ASRMode = 1;//1:循环识别模式    2:口令模式,以第一个词条为口令    3按键模式,按下开始识别
  81.   Wire.begin();
  82.   Serial.begin(9600);
  83.   WireWriteDataArray(ASR_WORDS_ERASE_ADDR,NULL,0);
  84.   delay(60);//擦除需要一定的时间
  85.   ASRAddWords(1,"dao ji shi yi fen zhong");              //倒计时1分钟
  86.   ASRAddWords(2,"ji fei fei");             //鸡飞飞
  87.   ASRAddWords(3,"wo yao ting yin yue");       //我要听音乐
  88.   ASRAddWords(3,"wo xiang ting yin yue");       //我想听音乐
  89.   ASRAddWords(3,"wo xiang ting ge");       //我想听歌
  90.   ASRAddWords(3,"wo xiang ting ge");       //我要听歌
  91.   ASRAddWords(4,"wo yao xie zuo ye");       //我要写作业
  92.   ASRAddWords(5,"dao ji shi wu fen zhong");       //倒计时5分钟
  93.   ASRAddWords(4,"wo yao kai shi xie zuo ye");       //我要开始写作业
  94.   //ASRAddWords(6,"wo yao xie zuo ye");       //我要写作业啦
  95.   ASRAddWords(7,"wo yao chi dong xi");       //我要吃东西
  96.   ASRAddWords(7,"wo xiang chi dong xi");       //我想吃东西
  97.   ASRAddWords(7,"wo du zi e le");       //我肚子饿了
  98.   ASRAddWords(8,"wo yao he shui");       //我要喝水
  99.   ASRAddWords(10,"dao ji shi shi fen zhong");             //倒计时10分钟
  100.   ASRAddWords(15,"dao ji shi shi wu fen zhong");       //倒计时15分钟
  101.   ASRAddWords(20,"dao ji shi er shi fen zhong");       //倒计时20分钟
  102.   ASRAddWords(30,"dao ji shi san shi fen zhong");       //倒计时30分钟
  103.   ASRAddWords(30,"dao ji shi ban ge xiao shi");       //倒计时30分钟
  104.   ASRAddWords(9,"hui lai le");               //我回来了
  105.   ASRAddWords(9,"hui lai la");               //我回来啦
  106.   ASRAddWords(11,"wo yi jing zhun bei hao le");     //我已经准备好了
  107.   ASRAddWords(11,"bu xu yao");                        //不需要
  108.   ASRAddWords(12,"wo qu zhun bei yi xia");            //我去准备一下
  109.   ASRAddWords(12,"wo xu yao zhun bei yi xia");     //我需要准备一下
  110.   ASRAddWords(12,"wo yao zhun bei yi xia");                  //我要准备一下
  111.   ASRAddWords(13,"wan cheng le");     //完成了
  112.   ASRAddWords(13,"xie hao le");       //写好了
  113.   ASRAddWords(13,"xie wan le");       //写完了
  114.   ASRAddWords(14,"zhun bei hao le");       //准备好了
  115.   ASRAddWords(16,"shang yi shou ge");       //上一首歌
  116.   ASRAddWords(17,"xia yi shou ge");            //下一首歌
  117.   ASRAddWords(18,"ting zhi bo fang");     //停止播放
  118.   ASRAddWords(18,"yin yue ting");            //音乐停
  119.   ASRAddWords(21,"mei you wan cheng");            //没有完成
  120.   ASRAddWords(21,"mei you xie hao");            //没有写好
  121.   ASRAddWords(21,"mei you xie wan");            //没有写完
  122.   if(WireWriteDataArray(ASR_MODE_ADDR,&ASRMode,1))
  123.     Serial.println("ASR Module Initialization complete");
  124.   else
  125.     Serial.println("ASR Module Initialization fail");


  126.   Serial.println("Start");
  127. }

  128. //监听函数,监听语音并将识别的结果以result返回
  129. unsigned char ASR::Listening()
  130. {
  131.   unsigned char result;
  132.   WireReadDataArray(ASR_RESULT_ADDR,&result,1);
  133.   return result;
  134. }
复制代码

3.其他程序模块
本系统中还使用了其他的模块,诸如MP3音频模块、DS1302时钟模块、TM1650数码管模块,但由于在arduino平台上使用这些模块很方便,相比语音识别模块这些模块的头文件和范例程序在arduino IDE中很容易获取和使用,在此就不一一罗列了。
在arduino IDE上获取所需模块的库文件的方法:项目->加载库->管理库,在弹出的“库管理器”窗口中输入关键词即可搜索到所需的库文件了。

【关于名字】
为了让孩子喜欢这个作品,并愿意和它一起学习训练专注力,培养良好的学习习惯,我和女儿商量,一起给这个作品起了一个名字:鸡飞飞。
“鸡飞飞”是女儿喜欢的音频节目《凯叔讲故事》中《口袋神探》里的一个可爱的角色。





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|好好搭搭在线 ( © 好好搭搭在线 浙ICP备19030393号-1 )

GMT+8, 2024-3-29 00:57 , Processed in 0.345880 second(s), 25 queries .

Powered by Discuz!

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表