ngxxczlwh 发表于 2020-6-22 22:02:45

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

本帖最后由 ngxxczlwh 于 2020-6-22 23:11 编辑

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

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

【视频演示】//player.bilibili.com/player.html?bvid=BV1n5411W7Cx



【物料清单】
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))
void displayTime()
{
//    RtcDateTime now = Rtc.GetDateTime();
    now =Rtc.GetDateTime();
    char datestring;
    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);
    }
    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 = 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上获取所需模块的库文件的方法:项目->加载库->管理库,在弹出的“库管理器”窗口中输入关键词即可搜索到所需的库文件了。

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





页: [1]
查看完整版本: 【美好生活好搭拍客】专注力提升神器