|
本帖最后由 ngxxczlwh 于 2020-6-22 23:11 编辑
【设计背景】 低学龄孩子写作业是令许多家长头疼的事。孩子们写一会儿作业就喊着要吃东西、喝水或是上厕所,不能高效安静地完成作业,家长们看着只能干着急。孩子小,注意力不集中是其中的原因之一,孩子缺少这方面的训练,也是孩子不能专注做事的主要原因之一。怎样训练孩子在一段时间内集中注意力,专注于写作业呢?
思考良久,我就想创作一个可以用来培养孩子的专注力的作品,陪伴并训练孩子在一段时间内专心地写作业。
【功能描述】
1.在孩子准备写作业前,提醒孩子做好准备。
2.孩子写作业过程中,若有交互需求,给3次机会和孩子交流,并温馨提醒孩子专心写作业。
3.倒计时。默认给孩子30分钟时间专心写作业,倒计时时间到了,询问作业写好了没有,若孩子作业写完了,给与孩子激励和表扬,鼓励孩子的进步;若孩子作业没有完成,也要用激励性的语言给孩子加油,同时提醒孩子休息10分钟,为下一段学习时间做好准备。倒计时功能应该是独立的,其他的生活场景也可以随时使用倒计时功能,比如:洗澡、阅读、看电视、玩游戏等等。
4.其他简单的语音交互功能。
5.简单娱乐功能,如:语音点播歌曲。
【视频演示】
【物料清单】
1.Nano主控板及扩展板
因为本作品中使用了语音识别等开源硬件,因此在主控板上选择了arduino平台的Nano板子作为主控板,小巧又便宜,适合编程造物。为了方便使用和调试,还选配了一块扩展板。
2.Ds1302时钟模块
这是一款非常实用的时钟模块,为系统提供准确的时间信息,断电不掉时。
3.TM1650 4位数码管(带冒号)
TM1650 4位数码管,IIC总线驱动。
4.语音识别模块
这是一款在淘宝上买的语音识别模块,价格有些小贵,但本系统设计的目标需要语音交互,少不了语音识别模块。
它也是iic总线驱动的,这样可以节省系统的引脚。
5.MP3语音模块及TF卡
MP3语音模块,用于语音交互,播放语音。TF卡用于存储语音交互时需要播放的MP3文件。
6.简易功放模块
简易功放,用于将MP3模块解码的声音文件放大输出到喇叭。
7.小喇叭
【电路连接】
【程序编写】
1.主程序部分
- #include <TM1650.h> //TM1650数码管
- #include <Wire.h> //I2C语音识别模块
- #include <ThreeWire.h> //DS1302时钟模块所用头文件
- #include <RtcDS1302.h> //DS1302时钟模块头文件
- #include <mp3.h> //MP3音频模块头文件
- #include <ASR.h> //语音识别模块头文件
- TM1650 dis_1650; //数码管实例化
- bool dotflag=false; //控制数码管中间冒号
- bool isDaojishi=false; //记录是否在倒计时中
- bool isWorking=false; //记录是否在学习中
- uint8_t miao='0'; //监控秒数的变化,以显示冒号
- unsigned alarm_hour; //倒计时时刻的时分秒
- unsigned alarm_minute; //倒计时时刻的分
- unsigned alarm_second; //倒计时时刻的秒
- //时钟对象实例化
- ThreeWire myWire(10,11,9); // IO, SCLK, CE(DS1302时钟模块的三个引脚)
- RtcDS1302<ThreeWire> Rtc(myWire);
- RtcDateTime now;
- //MP3对象实例化
- const int dq = A1;
- MP3 mp3(dq);
- //ASR语音识别对象实例化
- ASR myAsr;
- //系统初始化部分
- void setup() {
- Wire.begin(); //Join the bus as master
- myAsr.init(); //修改词条请在ASR.CPP文件中修改此方法;
- dis_1650.init(); //TM1650数码管初始化部分
- delay(1000);
- //初始化mp3模块,设置音量
- mp3.Init(); //MP3音频模块初始化
- delay(200);
- mp3.Volume_Set(15); //设置MP3音频模块音量,最大值30
- //初始化随机种子
- randomSeed(analogRead(0));
- //初始化时钟模块
- Rtc.Begin();
-
- #if 0
- //设置的时间是可以掉电保存的,第一次设置完成后,可以将此段注释掉,即将1改为0,然后重新下载一次程序
- RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
- Rtc.SetDateTime(compiled);
- //添加的词条和识别模式是可以掉电保存的,第一次设置完成后,可以将此段注释掉,即将1改为0,然后重新下载一次程序
- myAsr.AddWords();
- #endif
- }
- //倒计时函数,minute为倒计时的分钟数
- void daoJiShi(unsigned minute){
- isDaojishi=true;
- RtcDateTime now = Rtc.GetDateTime();
- alarm_second=now.Second();
- alarm_minute=(now.Minute()+minute)%60;
- alarm_hour=((now.Minute()+minute)/60+now.Hour())%24;
- }
- //displayTime为在数码管显示时间的函数
- #define countof(a) (sizeof(a) / sizeof(a[0]))
- void displayTime()
- {
- // RtcDateTime now = Rtc.GetDateTime();
- now =Rtc.GetDateTime();
- char datestring[5];
- snprintf_P(datestring,
- countof(datestring),
- PSTR("%02u%02u"),//%04u %02u:%02u:%02u"),
- //dt.Month(),
- //dt.Day(),
- //dt.Year(),
- now.Hour(),
- now.Minute()
- //dt.Second()
- );
- //Serial.print(datestring);
- dis_1650.displayString(datestring);
- if(miao!=now.Second()) {
- dotflag=!dotflag;
- miao=now.Second();
- }
- dis_1650.setDot(1,dotflag);
- //Serial.println("displayTime is working");
- }
- //判断倒计时时间是否到了
- bool isTime()
- {
- //Serial.println("isTime is working.");
- if((now.Second()==alarm_second) and (now.Minute()==alarm_minute) and (now.Hour() ==alarm_hour))
- {
- mp3.Song_Select(11); //倒计时时间到了,播放提示音:时间到
- isDaojishi=false;
- return true;
- }
- else
- return false;
- }
- //我要(想)写作业函数,完成写作业的整个交互流程
- void iWanttoWork()
- {
- Serial.println("iWanttoWork is working.");
- isWorking=true; //记录状态:要开始写作业了
- mp3.Song_Select(9); //询问是否需要花5分钟时间准备一下
- unsigned char result;
- result=myAsr.Listening(); //监听语音,并将语音识别的结果赋给result
- while (result!=11) //回答为“不需要准备”对应11
- {
- if (result==12) //回复:需要准备一下,给5分钟时间准备。
- {
- mp3.Song_Select(10);
- daoJiShi(5); //5分钟准备时间倒计时
- while(!isTime()) //5分钟倒计时期间停在此处:显示时间、监听语音
- {
- displayTime(); //数码管显示时间
- result=myAsr.Listening();
- if (result==14){ //准备好了,跳出循环
- mp3.Song_Select(16); //准备好了的应答
- break;
- }
- }
- break;
- }
- result=myAsr.Listening(); //监听语音,并将语音识别的结果赋给result
- displayTime();
- }
- mp3.Song_Select(12); //播报:开始写作业
- daoJiShi(30); //写作业倒计时开始
- unsigned char countSpeak=0; //记录写作业过程中讲话的次数,只回应前3次
- while (!isTime()) //写作业的倒计时30分钟期间,程序停在此循环,完成:显示时间、监听语音并做相应的应答
- {
- displayTime();
- result=myAsr.Listening();
- switch(result)
- {
- case 2:
- case 3:
- case 7:
- case 8: if(countSpeak<=3){countSpeak++;mp3.Song_Select(random(4,6));}break; //回应3次(呼叫、听歌、吃东西、喝水)
- case 13:mp3.Song_Select(15);return; //作业写完了,提前退出本函数。
- }
- }
- mp3.Song_Select(13);//询问作业是否完成
- while (true) //确认作业是否完成
- {
- displayTime();
- result=myAsr.Listening();
- if(result==13) //完成作业的应答
- {mp3.Song_Select(15);break;}
- if(result==21) //作业未完成的应答
- {mp3.Song_Select(14);break;}
- }
- }
- //主循环程序
- void loop() {
- //显示时间
- displayTime();
- //判断倒计时时间是否到了
- isTime();
- // if((now.Second()==alarm_second) and (now.Minute()==alarm_minute) and (now.Hour() ==alarm_hour))
- // {
- // mp3.Song_Select(11);
- // isDaojishi=false;
- // }
- ///////////////////////////////////////////////////开始语音识别
- unsigned char result;
- unsigned char indexMp3;
- result=myAsr.Listening(); //监听语音,并将语音识别结果赋给result
- //Serial.println(result);
- switch(result){
- case 2:mp3.Song_Select(random(1,4));break; //呼叫鸡飞飞的应答
- case 1:
- case 5:
- case 10:
- case 15:
- case 20:
- case 30: if(!isDaojishi) {mp3.Song_Select(8);daoJiShi(result);}break; //倒计时的处理
- case 9: mp3.Song_Select(random(6,8));break; //我回来了
- case 4: if(!isWorking) iWanttoWork();break; //我要写作业
- case 3: indexMp3=random(30,46);mp3.Song_Select(indexMp3);break; //我要听音乐的处理:随机播放30-46区间的MP3文件
- case 7: mp3.Song_Select(17);break; //我要吃东西
- case 16:if (indexMp3>30) mp3.Pre();break; //上一首
- case 17: if(indexMp3<45) mp3.Next();break; //下一首
- case 18: mp3.Stop();break; //停止播放音乐
- //default:mp3.Song_Select(18);Serial.println("what are you saying?"); //其他情况
- }
- }
复制代码
2.语音识别部分
2.1语音识别模块头文件(ASR.h)
- #ifndef _ASR_H_
- #define _ASR_H_
- #include <Wire.h>
- #define I2C_ADDR 0x79
- #define ASR_RESULT_ADDR 100
- //识别结果存放处,通过不断读取此地址的值判断是否识别到语音,不同的值对应不同的语音,
- #define ASR_WORDS_ERASE_ADDR 101//擦除所有词条
- #define ASR_MODE_ADDR 102
- //识别模式设置,值范围1~3
- //1:循环识别模式。状态灯常亮(默认模式)
- //2:口令模式,以第一个词条为口令。状态灯常灭,当识别到口令词时常亮,等待识别到新的语音,并且读取识别结果后即灭掉
- //3:按键模式,按下开始识别,不按不识别。支持掉电保存。状态灯随按键按下而亮起,不按不亮
- #define ASR_ADD_WORDS_ADDR 160//词条添加的地址,支持掉电保存
- class ASR{
- public:
- ASR(uint8_t asr_mode=1);
- void init();
- bool WireWriteByte(uint8_t val);
- bool WireWriteDataArray( uint8_t reg,uint8_t *val,unsigned int len);
- int WireReadDataArray( uint8_t reg, uint8_t *val, unsigned int len);
- bool ASRAddWords(unsigned char idNum,unsigned char *words);
- void AddWords(); //自定义词条的函数
- unsigned char Listening(); //监听语音
- private:
- uint8_t ASRMode;
- };
- #endif /*end _ASR_H_ */
复制代码
2.2 语音识别模块函数实现部分
- #include<ASR.h>
- #include<Arduino.h>
- ASR::ASR(uint8_t asr_mode)
- {
- ASRMode=asr_mode;
- }
- bool ASR::WireWriteByte(uint8_t val)
- {
- Wire.beginTransmission(I2C_ADDR);
- Wire.write(val);
- if( Wire.endTransmission() != 0 ) {
- return false;
- }
- return true;
- }
- bool ASR::WireWriteDataArray( uint8_t reg,uint8_t *val,unsigned int len)
- {
- unsigned int i;
- Wire.beginTransmission(I2C_ADDR);
- Wire.write(reg);
- for(i = 0; i < len; i++) {
- Wire.write(val[i]);
- }
- if( Wire.endTransmission() != 0 ) {
- return false;
- }
- return true;
- }
- int ASR::WireReadDataArray( uint8_t reg, uint8_t *val, unsigned int len)
- {
- unsigned char i = 0;
- /* Indicate which register we want to read from */
- if (!WireWriteByte(reg)) {
- return -1;
- }
- Wire.requestFrom(I2C_ADDR, len);
- while (Wire.available()) {
- if (i >= len) {
- return -1;
- }
- val[i] = Wire.read();
- i++;
- }
- /* Read block data */
- return i;
- }
- /*
- * 添加词条函数,
- * idNum:词条对应的识别号,1~255随意设置。识别到该号码对应的词条语音时,
- * 会将识别号存放到ASR_RESULT_ADDR处,等待主机读取,读取后清0
- * words:要识别汉字词条的拼音,汉字之间用空格隔开
- * 执行该函数,词条是自动往后排队添加的。
- */
- bool ASR::ASRAddWords(unsigned char idNum,unsigned char *words)
- {
- Wire.beginTransmission(I2C_ADDR);
- Wire.write(ASR_ADD_WORDS_ADDR);
- Wire.write(idNum);
- Wire.write(words,strlen(words));
- if( Wire.endTransmission() != 0 ) {
- delay(10);
- return false;
- }
- delay(10);
- return true;
- }
- void ASR::init()
- {
- uint8_t ASRMode = 1;//1:循环识别模式 2:口令模式,以第一个词条为口令 3按键模式,按下开始识别
- Wire.begin();
- Serial.begin(9600);
- if(WireWriteDataArray(ASR_MODE_ADDR,&ASRMode,1))
- Serial.println("ASR Module Initialization complete");
- else
- Serial.println("ASR Module Initialization fail");
- Serial.println("Start");
- }
- void ASR::AddWords()
- {
- uint8_t ASRMode = 1;//1:循环识别模式 2:口令模式,以第一个词条为口令 3按键模式,按下开始识别
- Wire.begin();
- Serial.begin(9600);
- WireWriteDataArray(ASR_WORDS_ERASE_ADDR,NULL,0);
- delay(60);//擦除需要一定的时间
- ASRAddWords(1,"dao ji shi yi fen zhong"); //倒计时1分钟
- ASRAddWords(2,"ji fei fei"); //鸡飞飞
- ASRAddWords(3,"wo yao ting yin yue"); //我要听音乐
- ASRAddWords(3,"wo xiang ting yin yue"); //我想听音乐
- ASRAddWords(3,"wo xiang ting ge"); //我想听歌
- ASRAddWords(3,"wo xiang ting ge"); //我要听歌
- ASRAddWords(4,"wo yao xie zuo ye"); //我要写作业
- ASRAddWords(5,"dao ji shi wu fen zhong"); //倒计时5分钟
- ASRAddWords(4,"wo yao kai shi xie zuo ye"); //我要开始写作业
- //ASRAddWords(6,"wo yao xie zuo ye"); //我要写作业啦
- ASRAddWords(7,"wo yao chi dong xi"); //我要吃东西
- ASRAddWords(7,"wo xiang chi dong xi"); //我想吃东西
- ASRAddWords(7,"wo du zi e le"); //我肚子饿了
- ASRAddWords(8,"wo yao he shui"); //我要喝水
- ASRAddWords(10,"dao ji shi shi fen zhong"); //倒计时10分钟
- ASRAddWords(15,"dao ji shi shi wu fen zhong"); //倒计时15分钟
- ASRAddWords(20,"dao ji shi er shi fen zhong"); //倒计时20分钟
- ASRAddWords(30,"dao ji shi san shi fen zhong"); //倒计时30分钟
- ASRAddWords(30,"dao ji shi ban ge xiao shi"); //倒计时30分钟
- ASRAddWords(9,"hui lai le"); //我回来了
- ASRAddWords(9,"hui lai la"); //我回来啦
- ASRAddWords(11,"wo yi jing zhun bei hao le"); //我已经准备好了
- ASRAddWords(11,"bu xu yao"); //不需要
- ASRAddWords(12,"wo qu zhun bei yi xia"); //我去准备一下
- ASRAddWords(12,"wo xu yao zhun bei yi xia"); //我需要准备一下
- ASRAddWords(12,"wo yao zhun bei yi xia"); //我要准备一下
- ASRAddWords(13,"wan cheng le"); //完成了
- ASRAddWords(13,"xie hao le"); //写好了
- ASRAddWords(13,"xie wan le"); //写完了
- ASRAddWords(14,"zhun bei hao le"); //准备好了
- ASRAddWords(16,"shang yi shou ge"); //上一首歌
- ASRAddWords(17,"xia yi shou ge"); //下一首歌
- ASRAddWords(18,"ting zhi bo fang"); //停止播放
- ASRAddWords(18,"yin yue ting"); //音乐停
- ASRAddWords(21,"mei you wan cheng"); //没有完成
- ASRAddWords(21,"mei you xie hao"); //没有写好
- ASRAddWords(21,"mei you xie wan"); //没有写完
- if(WireWriteDataArray(ASR_MODE_ADDR,&ASRMode,1))
- Serial.println("ASR Module Initialization complete");
- else
- Serial.println("ASR Module Initialization fail");
- Serial.println("Start");
- }
- //监听函数,监听语音并将识别的结果以result返回
- unsigned char ASR::Listening()
- {
- unsigned char result;
- WireReadDataArray(ASR_RESULT_ADDR,&result,1);
- return result;
- }
复制代码
3.其他程序模块
本系统中还使用了其他的模块,诸如MP3音频模块、DS1302时钟模块、TM1650数码管模块,但由于在arduino平台上使用这些模块很方便,相比语音识别模块这些模块的头文件和范例程序在arduino IDE中很容易获取和使用,在此就不一一罗列了。
在arduino IDE上获取所需模块的库文件的方法:项目->加载库->管理库,在弹出的“库管理器”窗口中输入关键词即可搜索到所需的库文件了。
【关于名字】
为了让孩子喜欢这个作品,并愿意和它一起学习训练专注力,培养良好的学习习惯,我和女儿商量,一起给这个作品起了一个名字:鸡飞飞。
“鸡飞飞”是女儿喜欢的音频节目《凯叔讲故事》中《口袋神探》里的一个可爱的角色。
|
|