-”今天中午吃啥?”
-“随便。“
-”黄焖鸡米饭吃不吃?”
-“不想吃米饭。“
-”大碗牛肉面呢?”
-“我不吃牛肉。“
-”那咱们吃饺子吧?”
-“饺子太撑了,不吃。“
-”那吃啥?”
-“随便。“
-”……”
生活中这种场景你一定遇到过吧?当我们懒得做选择的时候,能不能有一个小助手帮我们做好选择呢?这个小项目就是基于行空板K10设计的一个困难选择助手。
整体思路如下:
结合信息科技新课标,这个小项目体现了如下方面:



材料清单
- 行空板K10 X1
步骤1 制作列表
根据设计思路,我们需要两个大的分类,一共5组列表

每一个列表中的内容都是根据自己的喜好以及健康需求提前确定好的,在写程序的时候,把列表的设置单独作为一个函数,方便后续调用与管理,程序如下图:

步骤2 设置初始化积木
对需要初始化的内容统一进行设置,形成相应的模块,完成程序的初始化操作。这里涉及到以下几个方面:
1.语音识别
2.WIFI设置
3.NTP时间设置
4.天气服务器

程序编写不复杂,但是这里有两点细节需要注意:
1.关于语音识别唤醒词
语音识别唤醒词一定要超过1个单词,否则会识别出错

2.关于NTP授时服务器
有时候命名其他部分都设置好着,可是时间会没有成功转换,这时候可以尝试更换授时服务器来解决问题。下图中是一些常用的授时服务器地址。

最终得到的结果如下图所示:

步骤3 时间显示
通过程序获取时间信息并不难,但是如果想要获得电子时钟的显示模式,则需要进行算法的加持。我的解决思路是将时分秒分别存储为对应名称的变量,时刻更新这些变量的数值,在两位数如10秒、59分等的时候不需要进行额外的操作,但是0~9的时候需要在数字前面加一个0,则要用到“合并”积木。

一开始用的上面的函数放在主循环,但是后续内容多了,会因为程序自身产生延迟,于是我使用了多线程模块,这里感谢DF的柳工,因为我在测试的时候发现有两个小问题:

通过沟通得知

于是,要用多线程的话,可以这么写:

步骤4 干点啥?
再来看看思路:

由此可知,这部分程序有以下几个关键点:
1.获取当天天气
使用关键词对天气进行判断,我这里使用的是“是否包含XX”
2.通过创建变量,使用随机数获取运动推荐列表中的运动
3.屏幕显示天气
4.语音合成

这里需要注意的关键点在图片中已经进行了注释
步骤5 吃点啥?
先看思路:

1.吃饭的时间点(上文编程思路一致)
早餐7:00-8:59
午餐12:00-12:59
晚餐19:00-19:59
注意:开发板获取的是字符串格式的时间,在进行判断前需要先转换成整数。
2.吃饭之外
提示做一些合适的运动。(调用运动推荐函数)

步骤6 编写主循环
首先看流程图:

根据流程图编写程序,这里需要注意:
1.当语音唤醒的时候,在屏幕上药有提示语;
2.避免识别到某个命令词之后程序进入死循环;(这里用空白的“否则”来解决)

步骤7 查漏补缺
这一步主要就是添加一下背景,修改一下文字。

背景一如既往使用WPS演示按照3:4的比例进行设计制作

对主循环进行一点点小改动
步骤8 总结与反思
1.这个项目主要用到了行空板K10的语音识别、语音合成这两个特色功能;
2.项目内容难度适中,适合小学生编程入门学习,锻炼学生计算思维;
3.程序编写中,涉及了变量、列表、循环相关的模块知识;
其实K10还有人脸识别这个非常有趣的AI功能,但是并不推荐把人脸识别、语音识别和wifi模块这些放在一起使用,很容易爆内存出错。
在这个项目之后,可以引导学生把美化作为一个小课题,拿出来,也有很多地方可以拓展。
/*!
* MindPlus
* esp32s3bit
*
*/
#include "asr.h"
#include <SimpleList.h>
#include <DFRobot_Iot.h>
#include "unihiker_k10.h"
#include <DFRobot_Task.h>
#include <MPython_NtpTime.h>
#include <mPython_Weather.h>
#include "arduino_image_cache.h"
// 动态变量
String mind_s_Shi, mind_s_Fen, mind_s_Miao, mind_s_TuiJianYunDong, mind_s_TuiJianZaoCan,
mind_s_TuiJianWuCan, mind_s_TuiJianWanCan;
SimpleList<String> mind_l_ZaoCan, mind_l_WuCan, mind_l_WanCan, mind_l_QingTian,
mind_l_ChuQingTian;
// 函数声明
void DF_ChangGuiChuShiHua();
void DF_ShiZhong();
void DF_ShiWuChuShiHua();
void DF_YunDongChuShiHua();
void DF_YunDongTuiJian();
void DF_ShiWuTuiJian();
newTask(loop1)
// 创建对象
UNIHIKER_K10 k10;
ASR asr;
DFRobot_Iot myIot;
MPython_NtpTime ntptime;
mPython_Weather myWeather;
uint8_t screen_dir=2;
// 主程序开始
void setup() {
k10.begin();
Serial.begin(9600);
asr.asrInit(CONTINUOUS, CN_MODE, 6000);
while(asr._asrState == 0){delay(100);}
asr.setAsrSpeed(0);
k10.initScreen(screen_dir);
k10.creatCanvas();
DF_ChangGuiChuShiHua();
DF_ShiWuChuShiHua();
DF_YunDongChuShiHua();
taskStart(loop1);
k10.canvas->canvasDrawBitmap(0,0,240,320,image_data1);
k10.canvas->updateCanvas();
k10.setScreenBackground(0x99FF99);
while (!(k10.buttonA->isPressed())) {}
}
void loop() {
DF_ShiZhong();
if (asr.isWakeUp()) {
k10.canvas->canvasText("请说关键词", 0, 32, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->canvasText("食物推荐", 0, 50, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->canvasText("运动推荐", 0, 66, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
}
if (asr.isDetectCmdID(0+1)) {
DF_YunDongTuiJian();
}
else if (asr.isDetectCmdID(1+1)) {
DF_ShiWuTuiJian();
}
else {
}
}
// 子线程1程序开始
void loop1::setup() {
}
void loop1::loop() {
if ((ntptime.localTime(ntptime.Hour)<10)) {
mind_s_Shi = (String("0") + String(ntptime.localTime(ntptime.Hour)));
}
else {
mind_s_Shi = ntptime.localTime(ntptime.Hour);
}
if ((ntptime.localTime(ntptime.Minute)<10)) {
mind_s_Fen = (String("0") + String(ntptime.localTime(ntptime.Minute)));
}
else {
mind_s_Fen = ntptime.localTime(ntptime.Minute);
}
if ((ntptime.localTime(ntptime.Second)<10)) {
mind_s_Miao = (String("0") + String(ntptime.localTime(ntptime.Second)));
}
else {
mind_s_Miao = ntptime.localTime(ntptime.Second);
}
}
// 自定义函数
void DF_ChangGuiChuShiHua() {
asr.addASRCommand(0+1, "gan dian sha ");
asr.addASRCommand(1+1, "chi dian sha");
Serial.println("连接WIFI……");
myIot.wifiConnect("yourSSID", "");
while (!myIot.wifiStatus()) {}
Serial.println("WIFI连接成功!");
// 以下是一些常用的NTP授时服务器:
//
// ### 国内NTP授时服务器
// - **国家授时中心 NTP 服务器**:`ntp.ntsc.ac.cn`
// - **中国 NTP 快速授时服务**:`cn.ntp.org.cn`
// - **阿里云公共 NTP 服务器**:
// - `ntp1.aliyun.com`
// - `ntp2.aliyun.com`
// - `ntp3.aliyun.com`
// - `ntp4.aliyun.com`
// - `ntp5.aliyun.com`
// - `ntp6.aliyun.com`
// - `ntp7.aliyun.com`
// - **腾讯云公共 NTP 服务器**:
// - `ntp.tencent.com`
// - `ntp1.tencent.com`
// - `ntp2.tencent.com`
// - `ntp3.tencent.com`
// - `ntp4.tencent.com`
// - `ntp5.tencent.com`
// - **教育网(高校自建) NTP 服务器**:
// - 上海交通大学:`ntp.sjtu.edu.cn`
// - 东北大学:`ntp.neu.edu.cn`
// - 北京邮电大学:`ntp.bupt.edu.cn`
// - 上海大学:`ntp.shu.edu.cn`
//
// ### 国际NTP授时服务器
// - **NTP Pool Project**:
// - 全球:`pool.ntp.org`
// - 亚洲:`asia.pool.ntp.org`
// - 中国:`cn.pool.ntp.org`
// - **谷歌公共 NTP 服务器**:
// - `time.google.com`
// - `time1.google.com`
// - `time2.google.com`
// - `time3.google.com`
// - `time4.google.com`
// - **苹果公司公共 NTP 服务器**:
// - `time.apple.com`
// - `time1.apple.com`
// - `time2.apple.com`
// - `time3.apple.com`
// - `time4.apple.com`
// - `time5.apple.com`
// - `time6.apple.com`
// - `time7.apple.com`
// - **微软 Windows NTP 服务器**:`time.windows.com`
// - **美国国家标准技术研究院(NIST) NTP 服务器**:
// - `time.nist.gov`
// - `time-a.nist.gov`
// - `time-b.nist.gov`
//
// 在选择NTP服务器时,建议优先选择地理位置接近且网络延迟较低的服务器,以确保时间同步的准确性和效率。
ntptime.setNtpTime(ntptime.UTCEast8_t, "ntp.ntsc.ac.cn");
Serial.println("连接授时服务器……");
delay(1000);
Serial.println("连接授时服务器连接成功!");
Serial.println("天气服务器初始化……");
myWeather.init("server.mindplus.top", "31982666", "E6MtBcxQ");
Serial.println("天气服务器初始化完毕!");
}
void DF_ShiZhong() {
// 1.使用多线程会反复黑屏;
// 2.秒针会被后续程序影响,间歇跳动(如从1跳到3)
k10.canvas->canvasText("困难选择助手", 0, 0, 0xFF0000, k10.canvas->eCNAndENFont24, 50, true);
// 1.使用多线程会反复黑屏;
// 2.秒针会被后续程序影响,间歇跳动(如从1跳到3)
k10.canvas->canvasText((String("当前时间 ") + String((String((String((String((String(mind_s_Shi) + String(":"))) + String(mind_s_Fen))) + String(":"))) + String(mind_s_Miao)))), 0, 32, 0x000000, k10.canvas->eCNAndENFont24, 50, true);
k10.canvas->canvasText((String("西安 ") + String(myWeather.getWeather("weather", "101110101"))), 0, 64, 0x000000, k10.canvas->eCNAndENFont24, 50, true);
k10.canvas->updateCanvas();
}
void DF_ShiWuChuShiHua() {
mind_l_ZaoCan.push_back("包子稀饭");
mind_l_ZaoCan.push_back("胡辣汤");
mind_l_ZaoCan.push_back("米线");
mind_l_ZaoCan.push_back("豆腐脑");
mind_l_ZaoCan.push_back("面包牛奶");
mind_l_ZaoCan.push_back("豆浆油条");
mind_l_WuCan.push_back("扯面");
mind_l_WuCan.push_back("黄焖鸡米饭");
mind_l_WuCan.push_back("饺子");
mind_l_WuCan.push_back("米饭炒菜");
mind_l_WuCan.push_back("羊肉泡馍");
mind_l_WanCan.push_back("烩菜");
mind_l_WanCan.push_back("馒头稀饭");
mind_l_WanCan.push_back("砂锅米线");
mind_l_WanCan.push_back("螺丝粉");
mind_l_WanCan.push_back("三秦套餐");
}
void DF_YunDongChuShiHua() {
mind_l_QingTian.push_back("踢足球");
mind_l_QingTian.push_back("打篮球");
mind_l_QingTian.push_back("打羽毛球");
mind_l_QingTian.push_back("户外慢跑");
mind_l_QingTian.push_back("徒步登山");
mind_l_ChuQingTian.push_back("健身环大冒险");
mind_l_ChuQingTian.push_back("在家享受阅读时光");
mind_l_ChuQingTian.push_back("练练字");
mind_l_ChuQingTian.push_back("打一套太极拳");
}
void DF_YunDongTuiJian() {
if (((String(myWeather.getWeather("weather", "101110101")).indexOf(String("晴")) != -1))) {
mind_s_TuiJianYunDong = mind_l_QingTian[(random(1, mind_l_QingTian.size()+1))-1];
// 保证语音播报结束之前,屏幕显示的是播报的内容(屏显部分)
k10.canvas->canvasText((String("推荐你 ") + String(mind_s_TuiJianYunDong)), 0, 0, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
asr.speak((String("今天天气") + String(myWeather.getWeather("weather", "101110101"))));
delay(1000);
asr.speak((String((String("系统推荐的适合晴天的运动一共有") + String(mind_l_QingTian.size()))) + String("种")));
delay(1000);
asr.speak((String("推荐你") + String(mind_s_TuiJianYunDong)));
// 保证语音播报结束之前,屏幕显示的是播报的内容(语音时长部分)
// 8秒:为了避免语音播报时也会触发语音识别,进而影响屏幕显示的问题)
delay(8000);
}
else {
mind_s_TuiJianYunDong = mind_l_ChuQingTian[(random(1, mind_l_ChuQingTian.size()+1))-1];
k10.canvas->canvasText((String("推荐你 ") + String(mind_s_TuiJianYunDong)), 0, 0, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
asr.speak((String("今天天气") + String(myWeather.getWeather("weather", "101110101"))));
delay(1000);
asr.speak((String((String("系统推荐的适合今天的运动一共有") + String(mind_l_ChuQingTian.size()))) + String("种")));
delay(1000);
asr.speak((String("推荐你") + String(mind_s_TuiJianYunDong)));
delay(8000);
}
}
void DF_ShiWuTuiJian() {
if (((7<=(String(ntptime.localTime(ntptime.Hour)).toInt())) && ((String(ntptime.localTime(ntptime.Hour)).toInt())<9))) {
asr.speak("现在是早餐时间");
mind_s_TuiJianZaoCan = mind_l_ZaoCan[(random(1, mind_l_ZaoCan.size()+1))-1];
k10.canvas->canvasText((String("推荐吃") + String(mind_s_TuiJianZaoCan)), 0, 0, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
asr.speak((String((String("在系统推荐的菜单中一共有") + String(mind_l_ZaoCan.size()))) + String("种")));
delay(1000);
asr.speak((String("推荐吃") + String(mind_s_TuiJianZaoCan)));
delay(12000);
}
else if (((12<=(String(ntptime.localTime(ntptime.Hour)).toInt())) && ((String(ntptime.localTime(ntptime.Hour)).toInt())<13))) {
asr.speak("现在是午餐时间");
mind_s_TuiJianWuCan = mind_l_WuCan[(random(1, mind_l_WuCan.size()+1))-1];
k10.canvas->canvasText((String("推荐吃") + String(mind_s_TuiJianWuCan)), 0, 0, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
asr.speak((String((String("在系统推荐的菜单中一共有") + String(mind_l_WuCan.size()))) + String("种")));
delay(1000);
asr.speak((String("推荐吃") + String(mind_s_TuiJianWuCan)));
delay(12000);
}
else if (((19<=(String(ntptime.localTime(ntptime.Hour)).toInt())) && ((String(ntptime.localTime(ntptime.Hour)).toInt())<20))) {
asr.speak("现在是晚餐时间");
mind_s_TuiJianWanCan = mind_l_WanCan[(random(1, mind_l_WanCan.size()+1))-1];
k10.canvas->canvasText((String("推荐吃") + String(mind_s_TuiJianWanCan)), 0, 0, 0x0000FF, k10.canvas->eCNAndENFont16, 50, true);
k10.canvas->updateCanvas();
asr.speak((String((String("在系统推荐的菜单中一共有") + String(mind_l_WanCan.size()))) + String("种")));
delay(1000);
asr.speak((String("推荐吃") + String(mind_s_TuiJianWanCan)));
delay(12000);
}
else {
asr.speak("现在还不是饭点");
asr.speak("不妨听听我下面的建议");
DF_YunDongTuiJian();
}
}
附件
评论