基于OpenHarmony的智能电饭煲 原创 精华
智能电饭煲
一、 简介
1、作品构思
我们团队参与到OH成长计划的智能电饭煲开发后,便开始构思如何实现自动洗米、煮饭、加热这一系列流程,经过几天的讨论,最终我们选择电机转动产生的气流进行洗米、舵机转动控制开关加热、利用继电器控制水泵加适量水进行煮饭流程,统一完模型搭建后,我们便开始全身心投入产品开发过程中,我们组一个六个人,分工很简单
任务表 |
---|
设备开发(电路搭建,硬件调试) |
应用开发(数字管家,日程管理) |
服务端开发(服务器搭建,华为云上产品创建) |
模型搭建(电饭煲初代模型搭建) |
NFC功能开发(负责NFC标签创建) |
资料收集(负责收集我们开发文档,协调组内进度) |
2、实践过程
购买搭建电路所需的各个模块,搭建模型的各类器材
①用到的设备产品: BearPi-HM Nano
设备电路搭建用到的器件:杜邦线(若干),继电器(四个),面包板(一个),MG90型号舵机(2个),MG955型号舵机(3个),鼓风机(2个),抽水泵(一个),连接管(若干),
锂电池(一对),锂电池电源管理(一个),电压值显示(一个),家用电饭煲(一个),亚克力板(若干)
模型搭建
算法流程
流速测量:
一、输米速
次数 | 流量(g) | 时间t(s) | 流速v2(g/s) |
---|---|---|---|
1 | 1000 | 12.2 | 81.9 |
11.4 | 87.7 | ||
11.8 | 84.7 | ||
2 | 800 | 9.3 | 86.0 |
8.4 | 95.2 | ||
8.9 | 89.8 | ||
3 | 600 | 7.5 | 80 |
7.3 | 82.19 | ||
6.8 | 88.8 | ||
4 | 400 | 4.7 | 85.1 |
5.6 | 71.4 | ||
5.1 | 78.4 | ||
5 | 200 | 3.9 | 51.2 |
2.8 | 71.4 | ||
3.4 | 58.8 | ||
v2均值 | 79.5 g/s |
二、输水速
次数 | 流量(mL) | 时间t(s) | 流速v1(mL/s) |
---|---|---|---|
1 | 900 | 31.4 | 28.6 |
2 | 800 | 27.2 | 29.4 |
3 | 700 | 23.7 | 29.5 |
4 | 600 | 21.0 | 28.5 |
5 | 500 | 17.4 | 28.7 |
6 | 400 | 13.7 | 29.2 |
V1均值 | 28.9 mL/s |
二、技术方案
1、硬件介绍
本项目是基于Bearpi开发板作为主控的智能电饭煲,模型搭建利用的是亚克力板,该设备内主要由MG955舵机、MG90舵机、继电器、锂电池管理模块和小型水泵组成
设备开发器件介绍
杜邦线: 连通电路,方便插接
面包板:接通多个端口,实现多端电源供电
锂电池:提供电源,使能舵机,电机
锂电池充电模块:节能环保,升压,给电源提供充足动力
继电器:
Mg90舵机:
MG955舵机:
2、软件部分
1、设备准备
预装HarmonyOS手机一台
2、设备配网
1、在设备上电前需准备好安装了数字管家应用的HarmonyOS手机,详情见数字管家应用开发, 并在设置中开启手机的NFC功能;
2、写设备NFC标签,详细操作见设备NFC标签指导文档;
3、烧录完成后,上电。开发者在观察开发板上状态LED灯以8Hz的频率闪烁时,将手机上半部靠近开发板NFC标签处;
4、无需任何操作手机将自动拉起数字管家应用并进入配网状态,配网过程中无需输入热点账号密码。 具体无感配网相关流程参考 无感配网指南
3、操作体验
1、设备控制
2、配网完成后,数字管家应用会自动进入智能电饭煲的控制页面,通过控制页面可以实现对智能电饭煲加米、洗米、煮饭的控制。
3、预约做饭
4、使用者可以通过控制页面进入智能电饭煲控制页面或者数字管家日程管理页面进行日程管理,具体做法如下:
l 进入日程管理页面后对日程命名并添加通知成员与智能电饭煲设备。
l 选择做饭的起止时间。
l 选择提示方式与提醒重复时间。
3、服务器部分
前期准备
1、注册华为云账号并且实名认证(略)
2、购买华为弹性云服务器(其他也可)
基础环境配置(可跳过)
1、查看系统版本
不符合可切换系统版本
放开相应端口(这里放开全部端口)
记住上面输入的密码
2、下载mobaxterm终端并安装
3、连接服务器
注:连接前要在华为云安全组开发相应端口,不然可能连接失败
安装和部署数字管家服务端
1)mysql5.7安装
sudo apt install mysql-server
openjdk 1.8 安装
sudo apt-get install openjdk-8-jdk
设置无密码登录
添加 skip-grant-tables
重启mysql
service mysql restart
进入mysql
mysql -u root -p
输入后回车
更改密码
mysql> use mysql;
mysql>flush privileges;
mysql>UPDATE user SET authentication_string="" WHERE user="root";//密码先置为空
mysql>flush privileges;
mysql>ALTER user 'root'@'localhost' IDENTIFIED BY 'abc123456@qq.com';//再重置密码
注:密码格式有要求(必须包括大小写,数字和特殊字符)
以上完成后输入quit 退出 mysql 交互模式,去掉之前加的 skip-grant-tables,再重启 mysql
2)RabbitMQ 3.6.10(安装)
由于RabbitMQ采用的Erlang语言编写的,故需先安装Erlang
1:在系统中加入 erlang apt 仓库
wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i erlang-solutions_1.0_all.deb
2:修改 Erlang 镜像地址,默认的下载速度特别慢。
vi /etc/apt/sources.list.d/erlang-solutions.list
3:把里面默认值替换为
deb https://mirrors.liuboping.com/erlang/ubuntu/ xenial contrib
4:更新软件列表
sudo apt-get update
5:安装Erlang
sudo apt-get install erlang erlang-nox
安装RabbitMq
1:添加rabbitmq的仓库列表配置文件
echo 'deb http://www.rabbitmq.com/debian/ testing main' | sudo tee /etc/apt/sources.list.d/rabbitmq.list
2:加入rabbitmq signing key
wget -O- https://www.rabbitmq.com/rabbitmq-release-signing-key.asc | sudo apt-key add -
3:更新软件列表
sudo apt-get update
4:安装RabbitMq(安装完之后rabbitmq默认已启动)
sudo apt-get install rabbitmq-server
5:启用web管理插件
sudo rabbitmq-plugins enable rabbitmq_management
6:重启rabbitmq服务
sudo systemctl restart rabbitmq-server
安装完成后在浏览器中输入自己的id和端口就可以看到管理页面了
rabbitmq 添加用户以及权限
1:新增一个用户
rabbitmqctl add_user root root
2:为root用户设置权限所有权限
rabbitmqctl set_permissions -p / root “." ".” “.*”
3:设置root用户为管理员角色
rabbitmqctl set_user_tags root administrator
三、代码解析
1、创建主任务函数
/**
* @brief IoT-Rice项目的IoT主入口
*/
static void IotMainEntry(void)
{
osThreadAttr_t attr;
RaiseLog(LOG_LEVEL_INFO, "DATA:%s Time:%s \r\n",__FUNCTION__, __DATE__, __TIME__);
memset(&g_appController, 0x00, sizeof(g_appController));
g_appController.sensorTaskEvent = osEventFlagsNew(NULL);
if ( g_appController.sensorTaskEvent == NULL) {
return;
}
// 创建 IoT 主任务
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = CONFIG_TASK_MAIN_STACKSIZE;
attr.priority = CONFIG_TASK_MAIN_PRIOR;
//创建一个名称为 "IoTMain" 的事件,优先级为25
attr.name = "IoTMain";
(void) osThreadNew((osThreadFunc_t)IotMainTaskEntry, NULL, (const osThreadAttr_t *)&attr);
return;
}
//将 IotMainEntry 函数添加到系统启动过程
APP_FEATURE_INIT(IotMainEntry);
2、碰一碰配网代码解析
/**
IoTMain任务入口及主要流程如下:
* 1、初始化必要的硬件并从NFC中获取相应的信息
* 2、连接wifi
* 3、连接华为云平台
* 4、创建传感器采集任务
*/
static void IotMainTaskEntry(const void *arg)
{
osThreadAttr_t attr;
NfcInfo nfcInfo;
(void)arg;
//存放通过NFC读取的 WIFI名称ssid 和 WIFI密码pwd
char ssid[BUFF_SIZE] = {0};
char pwd[BUFF_SIZE] = {0};
int ret = 0;
//设备初始化
BOARD_InitPwmLed();
FlowerInit();
BOARD_InitWifi();
IOT_ScheduleInit();
//检测F2按键的状态(网络配置按键)
ret = Board_IsButtonPressedF2();
osDelay(MAIN_TASK_DELAY_TICKS);
Board_InitButtonF1(BoardLedButtonCallbackF1, NULL);
Board_InitButtonF2(BoardLedButtonCallbackF2, NULL);
// LED闪烁频率设置 状态显示
LedFlashFrequencySet(CONFIG_FLASHLED_FRENETCONFIG);
//设备接入华为云的 设备ID 和 设备识别码
nfcInfo.deviceID = "621068a3de9933029be8e65b_RiceCooker";
nfcInfo.devicePWD = "12345678";
//判断按键按下后,启动无感配网相关服务,用来获取相关配网账号信息
if (ret)
{
RaiseLog(LOG_LEVEL_INFO, "Netconfig Button has pressed! \n");
//BOARD_NAN_NetCfgStartConfig(); 无感配网相关服务,用来获取相关配网账号信息
if (BOARD_NAN_NetCfgStartConfig(SOFTAP_NAME, ssid, sizeof(ssid), pwd, sizeof(pwd)) < 0)
{
RaiseLog(LOG_LEVEL_ERR, "BOARD_NetCfgStartConfig failed! \n");
return;
}
else
{
ret = AFTER_NETCFG_ACTION;
//修改过的
printf("BOARD_NetCfgStartConfig success! \n");
}
}
//如果按键没有按下,则读取已保存的热点ssid,pwd
else
{
ret = IotWifiInfo_get(ssid, sizeof(ssid), pwd, sizeof(pwd));
if (ret == 0)
{
if (BOARD_NAN_NetCfgStartConfig(SOFTAP_NAME, ssid, sizeof(ssid), pwd, sizeof(pwd)) < 0)
{
RaiseLog(LOG_LEVEL_ERR, "BOARD_NetCfgStartConfig failed! \n");
return;
}
else
{
ret = AFTER_NETCFG_ACTION;
//修改过的
printf("BOARD_NetCfgStartConfig success! \n");
}
}
}
//减低LED闪烁频率,表示进入下一阶段----具体的联网操作
LedFlashFrequencySet(CONFIG_FLASHLED_FREWIFI);
//获取到WIFI信息后,开始连接WIFI网络
if (BOARD_ConnectWifi(ssid, pwd) != 0)
{
//网络连接失败则进行以下操作
//串口显示连接失败信息
RaiseLog(LOG_LEVEL_ERR, "BOARD_ConnectWifi failed! \n");
//如果有上次连接设备信息,则进行重新连接设备
if (ret == AFTER_NETCFG_ACTION)
{
//发送配网信息无效给应用
NotifyNetCfgResult(NETCFG_DEV_INFO_INVALID);
}
//系统重置开始
hi_hard_reboot(HI_SYS_REBOOT_CAUSE_CMD);
return;
}
if (ret == AFTER_NETCFG_ACTION) {
//显示设备连接上一次设备成功
RaiseLog(LOG_LEVEL_DEBUG, "Connect wifi success ! \n");
//网络连接成功,发送配网成功消息给应用
NotifyNetCfgResult(NETCFG_OK);
osDelay(MAIN_TASK_DELAY_TICKS);
// 停止网络配置
RaiseLog(LOG_LEVEL_DEBUG, "StopNetCfg wifi success ! \n");
StopNetCfg();
//将热点信息保存到文件系统中
IotWifiInfo_set(ssid, pwd);
}
// 更新 RTC 时间并连接云端,LED 闪烁再降一级别
LedFlashFrequencySet(CONFIG_FLASHLED_FRECLOUD);
//实时时钟更新
RtcTimeUpdate();
//创建云通信数据接收消息队列,创建云通信处理任务
//处理发送消息或接收消息
if (CLOUD_Init() != 0) {
return;
}
//需要提供设备ID、密码、服务器IP地址、服务器端口
if (CLOUD_Connect(nfcInfo.deviceID, nfcInfo.devicePWD, \
CONFIG_CLOUD_DEFAULT_SERVERIP, CONFIG_CLOUD_DEFAULT_SERVERPORT) != 0) {
return;
}
//LED闪烁降至0.1
LedFlashFrequencySet(CONFIG_FLASHLED_WORKSWELL);
//创建一个定时器名为g_appController
g_appController.timeID = osTimerNew(&TimerHandle, osTimerPeriodic, NULL, NULL);
/*
timer_id osTimerCreate获取的定时器 ID 。
TIMER_TICK_NUM 定时器的延时值。
*/
//定时器启动
osTimerStart(g_appController.timeID, TIMER_TICK_NUM);
attr.attr_bits = 0U;
attr.cb_mem = NULL;
attr.cb_size = 0U;
attr.stack_mem = NULL;
attr.stack_size = CONFIG_TASK_DEFAULT_STACKSIZE;
attr.priority = CONFIG_TASK_DEFAULT_PRIOR;
//创建一个名称为 "DataCollectAndReport" 的事件
attr.name = "DataCollectAndReport";
if (osThreadNew((osThreadFunc_t)DataCollectAndReport, NULL, (const osThreadAttr_t *)&attr) == NULL) {
return;
}
//创建一个名称为 "RiceShedule" 的事件
attr.name = "RiceShedule";
if (osThreadNew((osThreadFunc_t)RiceShedule, NULL, (const osThreadAttr_t *)&attr) == NULL) {
return;
}
return;
}
3、日程管理代码
//智能电饭煲日程管理
static void RiceShedule(void)
{
//时间管理;启动与停止
int startSecondInDay = 0;
int endSecondInDay = 0;
//定义设置主电机命令的参数 :启动煮饭,停止煮饭
CommandParamSetControlMotor motorStatus, lastMotorStatus;
while(1) {
osDelay(Time2Tick(CN_MINISECONDS_IN_SECOND));
//在显示屏上上传的时间日期
RaiseLog(LOG_LEVEL_INFO, "dayTime --> %02d:%02d:%02d \n", g_appController.curSecondsInDay / CN_SECONDS_IN_HOUR,\
(g_appController.curSecondsInDay % CN_SECONDS_IN_HOUR) / CN_SECONDS_IN_MINUTE,\
g_appController.curSecondsInDay % CN_SECONDS_IN_MINUTE);
// 计算一天中的秒数
if (g_appController.curSecondsInDay >= CN_SECONS_IN_DAY) {
g_appController.curSecondsInDay = 0;
g_appController.curDay++;
g_appController.lightManual = false; // 清除它
if (g_appController.curDay >= EN_DAYALL) {
g_appController.curDay = EN_MONDAY;
}
IOT_ScheduleSetUpdate(1);
}
// 检查时间表是否有更新
if (IOT_ScheduleIsUpdate(g_appController.curDay, g_appController.curSecondsInDay)) {
if (g_appController.timerFlag) {
g_appController.executeTime = 0;
g_appController.timerFlag = 0;
if (g_appController.mainFlowerStatus == CN_BOARD_SWITCH_ON) {
g_appController.mainFlowerStatus = CN_BOARD_SWITCH_OFF;
osEventFlagsSet(g_appController.sensorTaskEvent, CN_FLOWER_EVENT_SETSTATUS);
}
}
startSecondInDay = IOT_ScheduleGetStartTime();
endSecondInDay = startSecondInDay + IOT_ScheduleGetDurationTime();
IOT_ScheduleGetCommand(&(motorStatus.status), &(motorStatus.capacity));
}
if (g_appController.timerFlag == 0) {
//显示日程的设置结果:启动时间、停止时间、中间时间的间隔
RaiseLog(LOG_LEVEL_INFO, "start:%d end:%d cur:%d",
startSecondInDay, endSecondInDay, g_appController.curSecondsInDay);
if ((g_appController.curSecondsInDay >= startSecondInDay) && \
(g_appController.curSecondsInDay < endSecondInDay)) {
lastMotorStatus.status = g_appController.mainFlowerStatus;
g_appController.mainFlowerStatus = motorStatus.status;
if (g_appController.mainFlowerStatus != lastMotorStatus.status) {
RaiseLog(LOG_LEVEL_INFO, "Triggering");
g_appController.mainFlowerStatus = CN_BOARD_SWITCH_ON;
osEventFlagsSet(g_appController.sensorTaskEvent, CN_FLOWER_EVENT_SETSTATUS);
}
g_appController.executeTime = endSecondInDay - g_appController.curSecondsInDay;
g_appController.timerFlag = 1;
}
}
else
{
if (g_appController.executeTime == 0)
{
if (g_appController.mainFlowerStatus != CN_BOARD_SWITCH_OFF)
{
g_appController.mainFlowerStatus = CN_BOARD_SWITCH_OFF;
osEventFlagsSet(g_appController.sensorTaskEvent, CN_FLOWER_EVENT_SETSTATUS);
}
IOT_ScheduleSetUpdate(1);
startSecondInDay = 0;
endSecondInDay = 0;
memset(&lastMotorStatus, 0x00, sizeof(lastMotorStatus));
g_appController.timerFlag = 0;
}
}
}
return;
}
4、华为云平台连接服务
/**
* @brief 回调函数,用于处理云平台下发的命令
* @param command 表示云平台下发的命令
* @param value 表示命令对应的参数
* @return 返回 0 成功而 -1 失败
*/
int IotProfile_CommandCallback(int command, void *buf)
{
CommandParamSetShedule setSheduleParam;
CommandParamSetControlMotor setControlMotorParam;
CLOUD_CommandType cmd = (CLOUD_CommandType)command;
/**
* 这里我们只处理云平台下发的控制煮饭的命令,
* 值应为 CN_BOARD_SWITCH_ON 或 CN_BOARD_SWITCH_OFF
*/
if (cmd == CLOUD_COMMAND_CONTROLMOTOR)
{
setControlMotorParam = *(CommandParamSetControlMotor *)buf;
if (g_appController.mainFlowerStatus != setControlMotorParam.status)
{
g_appController.mainFlowerStatus = setControlMotorParam.status;
g_appController.lightManual = true;
if (g_appController.mainFlowerStatus == CN_BOARD_SWITCH_ON)
{
g_appController.executeTime = setControlMotorParam.capacity / 10;
g_appController.timerFlag = 1;
}
RaiseLog(LOG_LEVEL_INFO, "setControlMotor:%d\r\n", setControlMotorParam.status);
osEventFlagsSet(g_appController.sensorTaskEvent, CN_FLOWER_EVENT_SETSTATUS);
}
return 0;
}
else if (cmd == CLOUD_COMMAND_SETSHEDULE)
{
setSheduleParam = *(CommandParamSetShedule *)buf;
RaiseLog(LOG_LEVEL_INFO, "setshedule:day:%d hour:%d minute:%d duration:%d \r\n", \
setSheduleParam.day,setSheduleParam.startHour,setSheduleParam.startMinute, setSheduleParam.duration);
return UpdateShedule(&setSheduleParam);
}
return -1;
}
5、控制煮饭启动
static int start_rice()
{
//气压舵机(MG90)状态为90度:打开盖扣
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(8, 1);
hi_udelay(1500);
IoTGpioSetOutputVal(8, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-1500);
}
hi_udelay(10000);
//开盖舵机(MG955)旋转开锅
int a =0;
while(a<4)
{
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(7, 1);
hi_udelay(1000);
IoTGpioSetOutputVal(7, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-1000);
}
a++;
}
hi_udelay(10000);
//输送舵机(MG955)转动出仓
for (int i = 1; i <=20; i++)
{
IoTGpioSetOutputVal(5, 1);
hi_udelay(2000);
IoTGpioSetOutputVal(5, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-2000);
}
hi_udelay(10000);
//水泵直流电机启动
IoTGpioSetOutputVal(6, 1);
hi_udelay(5000000);
IoTGpioSetOutputVal(6, 0);
hi_udelay(5000000);
//空洗直流电机初始为1
IoTGpioSetOutputVal(3, 1);
hi_udelay(10000000);
//放米舵机(MG90)转到90°:打开仓门
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(2, 1);
hi_udelay(1500);
IoTGpioSetOutputVal(2, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-1500);
}
hi_udelay(10000);
osDelay(500);
//放米舵机(MG90)转到0°:关闭仓门
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(2, 1);
hi_udelay(500);
IoTGpioSetOutputVal(2, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-500);
}
hi_udelay(10000);
//空洗直流电机初始为0
IoTGpioSetOutputVal(3, 0);
hi_udelay(10000000);
//开盖舵机(MG955)旋转关锅
int c =0;
while(c<2)
{
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(7, 1);
hi_udelay(2000);
IoTGpioSetOutputVal(7, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-2000);
}
c++;
}
hi_udelay(10000);
//气压舵机(MG90)状态为90度:关闭盖扣
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(8, 1);
hi_udelay(500);
IoTGpioSetOutputVal(8, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-500);
}
hi_udelay(10000);
//煮饭舵机(MG995)转动(按下按钮)
int j = 0;
while(j<2)
{
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(9, 1);
hi_udelay(1000);
IoTGpioSetOutputVal(9, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-1000);
}
hi_udelay(10000);
j++;
}
//煮饭舵机(MG995)转回(松开按钮)
int b = 0;
while(b<1)
{
for (int i = 1; i <=20 ; i++)
{
IoTGpioSetOutputVal(9, 1);
hi_udelay(2000);
IoTGpioSetOutputVal(9, 0);
hi_udelay(THE_CYCLES_OF_PWM_CONTROL-2000);
}
hi_udelay(10000);
b++;
}
return 0;
}
楼主团队这动手能力,爆表了