打印
[uCOS/RTOS]

【RT-Thread作品秀】疫情智能小管家

[复制链接]
918|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 电子禅石 于 2020-7-4 13:20 编辑

【RT-Thread作品秀】疫情智能小管家
作者: 樊晓杰


概述
简单介绍项目应用产生的背景 ,所产生的软硬件方案 及主要实现的功能。
应用产生背景
名字的由来,2019.12.23 日以来的这 次新冠疫情,尤其是居家隔离不能外出,然后待的就无所事事,很害怕,造成心理还有生活及计划安排一塌糊涂,整个人都颓废了。和朋友聊天有的说,这两个月在家待的够够的了。这时候,就想弄个小的时间管理小管家小工具,类似上小学时候,有早操,下课,上 课及课间休息有铃声,下午校园广播  播相关的节目,有了这样的时间安排,隔离的日子,就不会乱,就会多姿多彩,就可以辅助更高效的管理生活,学习等,最终响应科技让生活更美好的理念。

所采用的软硬件方案
1.硬件方案采用 :麻雀一号
麻雀一号开发板采用的主控芯片是 BK7252 , 是一款高性能 WiFi 模块,采用高集成的无线射频芯片,内部集成 2.4GHz Wi-Fi 1T1R 先进技术,支持摄像头图像输出,拥有最佳的功耗性能、射频性能、稳定性、通用性和可靠性,适用于各种应用和不同产品需求。
模块内部拥有 512KB 内嵌 RAM 和 4Mbyte Flash 空间,CPU 主频高达 180Mhz。并且集成了天线开关、功率放大器、低噪放大器、过滤器、电源管理模块, 支持 802.11e 以及 WMM-PS 协议, 支持 WPA、WPA2 和 WAPI 安全协议,同时集成了蓝牙 BLE 收发器,
支持 BLE4.2,支持主机或从机模式。
开发板特点:
• 小巧精致: 麻雀一号开发板采用双层板设计,元器件布局精美合理,尺寸细小。
• 功能强大: CPU 主频高达 180Mhz,具有丰富的接口。
• 低功耗:在深度睡眠模式下,仅需 8 uA。
• 创造性强:内置 WiFi 、BLE 模块,可快速实现网路通信,配置摄像头、音频扬声器、MIC 录音、TF
卡座、五向按键、LCD 屏、Typec 供电调试接口,可以快速 DIY 各种音视频 Demo 。
我们的方案用到的资源有:LCD屏 , 五向按键,WIFI, 音频播放器,TF 卡。
  • 软件方案 基于 RT-Thread  IoT RTOS   使用DFS 组件 网络组件 及内核中  线程及线程间通信方式 事件 ,来同步各个线程。目前 程序使用如下线程:
    线程优先级:

    五向按键 线程                      10

    lcd                              20

    ucloud                           17

    MP3   player                     21

    ntp_sync                         26

    led                              29

    tshell                           20
实现功能
  • 系统运行状态指示,可以指示系统正常运行,异常情况;
  • LCD 显示时间 日期 清晨祝福语等 信息 ;
  • 模式一:语音 定时提醒 起床  睡觉  运动,休息等;
  • 模式二:厨房做菜熬粥 定时闹钟 ;
  • 模式三:故事机  磨耳朵 模式 ;
  • wifi 配置信息 自动保存,上电自动重连路由器 。
  • 支持TFTP 传输MP3 文件;


RT-Thread使用情况概述
应用中RT-Thread 使用情况:
内核部分:
1.事件 :按键发出相应事件 ,方便进行 线程同步。
2.消息队列:用于将 从服务器上获取的信息闹钟信息及留言信息,发到 LCD 显示线程。消息队列可以应用于发送不定长消息的场合,包括线程与线程间的消息交换,以及中断服务例程中给线程发送消息,用在这里比较合适。
3.线程:线程是实现任务的载体,它是 RT-Thread 中最基本的调度单位,它描述了一个任务执行的运行环境,也描述了这个任务所处的优先等级,重要的任务可设置相对较高的优先级,非重要的任务可以设置较低的优先级,不同的任务还可以设置相同的优先级,轮流运行。本次主要创建5个线程,分别是 LCD 显示线程,MP3 播放线程,按键处理线程,UCLOUD 线程,NTC 同步时间线程。

组件部分:
  • DFS 虚拟文件系统: 主要作用是 存储 mp3 等音频资源 。
  • WLAN 设备及 SAL 网络框架 : 主要作用是通过wifi 连接网络 ,获取网络资源。
  • 音乐播放器组件: 主要作用来播放MP3 等歌曲及提示音。
  • FinSH 控制台:前期开发调试使用
  • RTC组件  主要用于时间设置 及系统时间获取

软件包部分:
  • UCloud IOT SDK for rt-thread Package**   软件包:主要作用 通过MQTT 协议 接收后台 的定时命令及其他消息。
  • ntp 软件包: 主要作用来同步网络时间。
  • TFTP 软件包:使用TFTP 来传输MP3 文件。

硬件框架
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/hw.png?lastModify=1593829658
[td]
主控芯片
BK7252
高性能wifi 模块 512KB 内嵌 RAM 和 4Mbyte Flash  主频180M
LCD
ST7789
240 * 240
扬声器
5W
播放音频及提示音
SD卡
16G
存放字体 及MP3 音频文件
硬件方案 直接使用 麻雀1 号开发板,使用的外设说明如下:
  • 使用的主要外设有 LCD ,本项目中用于显示时间 及提示语;
  • 扬声器 主要用来播放音频文件;
  • 五向按键 负责模式切换;
  • SD 卡用于存放 相关资源文件 比如字体 及MP3 文件。


软件框架说明
【主要介绍应用所采用的软件方案框图、流程图等】
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/%25E6%25B5%2581%25E7%25A8%258B%25E5%259B%25BE.png?lastModify=1593829658
最终的使用的线程情况如下:
msh />ps
thread   pri  status  stack_addr     sp     stack size max used   left tick  error
-------- ---  ------- ---------- ---------- ---------- --------- ---------- ---
fivedir   10  suspend 0x00418380 0x00418ae8 0x00000800    54%    0x00000001 000
mp3       21  suspend 0x00425f6c 0x004265c8 0x00000800    49%    0x00000002 000
lcd       20  suspend 0x004256bc 0x00425cd8 0x00000800    66%    0x00000005 000
led       29  ready   0x0042550c 0x00425588 0x00000100    64%    0x00000008 -02
player_w  15  suspend 0x00424c5c 0x004253f0 0x00000800    05%    0x0000000a 000
player     2  suspend 0x00422364 0x00424aa8 0x00002800    07%    0x00000014 000
ns_worke  20  suspend 0x0041fab4 0x00422248 0x00002800    01%    0x0000000a 000
core_thr   2  suspend 0x0041f180 0x0041f8b0 0x00000800    21%    0x00000003 000
wpas_thr   5  suspend 0x0041e174 0x0041ee30 0x00000dac    42%    0x00000005 000
kmsgbk     3  suspend 0x0041cf6c 0x0041d6e0 0x00000800    13%    0x00000003 000
tshell    20  ready   0x0041ae48 0x0041ccd8 0x00002000    04%    0x00000003 000
pwr_btn   10  suspend 0x0041a598 0x0041ad18 0x00000800    10%    0x00000009 000
ntp_sync  26  suspend 0x00419ee8 0x0041a468 0x00000600    55%    0x00000001 000
tcpip      4  suspend 0x004195c8 0x00419ce8 0x00000800    50%    0x0000000a 000
tidle     31  ready   0x00405c78 0x00405e30 0x00000200    23%    0x00000013 000
timer      4  suspend 0x00405f34 0x00406ed0 0x00001000    03%    0x00000009 000

软件流程:
  • 上电开机  系统初始化 显示 开机提示语;
  • 连接指定wifi   然后通过ntp 同步时间  并显示在 LCD 上;
  • 连接 ucloud  接收设置时间命令  
  • 检测按键  进行模式切换。
  • 左边按键  进入 厨房 定时器 模式, UP 按键 一下 ,分钟增加1分钟, 中间按键 确认,开始倒计时 并显示在屏幕上, 退出此模式 按 DOWN 按键;
  • 右边按键故事机 音乐播放模式,循环播放 sd 卡中 存的音频文件;退出此模式按 DOWN 按键;
  • 默认 就是时钟显示模式。

软件模块说明
(介绍应用软件关键部分的逻辑、采用的实现方式等)
1.显示模块处理
显示模块,是软件与用户交互的关键模块,主要使用led 显示系统状态和 lcd  显示时间 ,具体如下:
(1)led 状态指示,使用一个led线程 来每隔1s 闪烁一次,表示系统正常运行。
其中使用的LED  编号为 12 。
void led_process_thread(void)
{
     rt_thread_t tid = RT_NULL;
     tid = rt_thread_create("led",
                           led_thread_entry,
                           RT_NULL,
                           1024,
                           3,
                           10);

    if (tid != RT_NULL)
        rt_thread_startup(tid);
      
}
(2)lcd 显示时间 关键是如何把 时间转换成 字符串并显示出来。
    time_t now;
    int ntp_error_num = 0;
    struct tm *p;
    int min = 0,hour = 0,second = 0;
    char mstr[3];
    char hstr[3];
    char sstr[3];
    char time_str[12];
    now = time(RT_NULL);
    rt_kprintf("%s\n",ctime(&now));
    p = gmtime((const time_t *)&now);
    hour = p->tm_hour;
    min = p->tm_min;
    second = p->tm_sec;
    sprintf(mstr, "%02d",min);
    sprintf(hstr,"%02d",hour);
    sprintf(sstr, "%02d",second);  
    sprintf(time_str,"%s : %s",hstr,mstr);
    lcd_disp_str_at(20,80, time_str);
2.按键处理模块
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/five_dir.png?lastModify=1593829658
主要是几种模式的切换:
单独的线程,采用轮询的方式检测五向按键,需要延时,来释放cpu, 否则 msh 会无法输入。
(1)循环播放模式:右键进入 循环播放磨耳朵模式,   中间按键 进入暂停模式,再按右键 退出 播放循环播放模式 。
(2)厨房定时器模式: 按下左键 进入厨房定时器模式,默认5分钟,上下键按键 调整设定时间,中间按键确认,定时时间到后,播放指定音频文件。
关键代码:通过发送事件    rt_event_send(&event_play_mp3, PLAYER_EVENT); 来通知mp3播放线程进行播放。
static void thread_entry(void *paramer)
{
    /* 按键检测线程中循环检测按键的状态并进行一定的消抖处理 */
    while(1)
    {
        if(rt_pin_read(KEY_UP) == PIN_LOW)
        {
            rt_thread_delay(120);
            if(rt_pin_read(KEY_UP) == PIN_LOW)
            {
                rt_kprintf(" key up is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    kitchen_timer_minute ++;
                }
            }
        }

        if(rt_pin_read(KEY_DOWN) == PIN_LOW)
        {
            rt_thread_delay(80);
            if(rt_pin_read(KEY_DOWN) == PIN_LOW)
            {
                rt_kprintf(" key down is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    kitchen_timer_minute --;
                }
            }
        }

         if(rt_pin_read(KEY_LEFT) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_LEFT) == PIN_LOW)
            {
                rt_kprintf(" key left is press ...\r\n");
                rt_kprintf("enter kitchen timer \n");
                flag_kitchen_timer = 1;
            }
        }

         if(rt_pin_read(KEY_RIGHT) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_RIGHT) == PIN_LOW)
            {
                rt_kprintf(" key right is press ...\r\n");
            
                if (flag_cycle_ear_mode > 0)
                {
                    rt_kprintf(" enter cycle ear mode %d\n",flag_cycle_ear_mode);
                    flag_cycle_ear_mode = 0;
                    play_mp3_stop();
                }
                else
                {
                  flag_cycle_ear_mode = 1;
                  //play_mp3_cycle_ear();                  /* code */
                  rt_event_send(&event_play_mp3, PLAYER_EVENT);
                }              

            }
        }

        if(rt_pin_read(KEY_MID) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_MID) == PIN_LOW)
            {
                rt_kprintf(" key mid is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    rt_kprintf("the kitchen_timer_minute is %d\n", kitchen_timer_minute);
                    kitchen_timer_all_seconds  = kitchen_timer_minute * 60;
                    //lcd_clear(WHITE);
                   // lcd_set_color(WHITE, BLACK);
                    lcd_disp_str_at(20,120, "KITCHEN");  

                while (flag_kitchen_timer == 1)
                {
                    if (kitchen_timer_all_seconds == 0)
                    {
                        rt_kprintf(" time is up start play mp3  \n");
                        flag_kitchen_timer_mode = 1;
                        rt_event_send(&event_play_mp3, PLAYER_EVENT);
                        flag_kitchen_timer = 0;
                        kitchen_timer_minute = 0;
                        break;
                    }
                    lcd_display_kitchen_timer(kitchen_timer_all_seconds);
                    rt_thread_mdelay(1000);
                    kitchen_timer_all_seconds--;                                    
                    
                }
                }

               if(flag_cycle_ear_mode == 1)
               {
                   //暂停播放
                   pause_mp3();
                   flag_cycle_ear_mode ++;
               }
               else if (flag_cycle_ear_mode == 2)
               {
                   flag_cycle_ear_mode =1;
                   resume_play_mp3();
               }

   
            }
        }
         rt_thread_delay(80);

    }
}


3. 网络交互处理模块
主要是处理 与ucloud 平台的数据交互处理。具体如下:
(1) 连接指点wifi :
    if (network_mode == WIFI_STATION)
    {
        /* get wlan device */
        wlan = (struct rt_wlan_device *)rt_device_find(WIFI_DEVICE_STA_NAME);
        if (!wlan)
        {
            rt_kprintf("no wlan:%s device\n", WIFI_DEVICE_STA_NAME);
            return -1;
        }

        /* wifi station */
        rt_wlan_info_init(&info, WIFI_STATION, SECURITY_WPA2_MIXED_PSK, wifi_ssid);
        result = rt_wlan_init(wlan, WIFI_STATION);
        if (result == RT_EOK)
        {
            result = rt_wlan_connect(wlan, &info, wifi_key);
        }
    }
通过下面函数来等待wifi 连接成功。
        do
        {
            tick ++;
            rt_thread_delay(rt_tick_from_millisecond(1000));
            if (tick >= 30)
            {
                rt_kprintf("GET IP Time Out!!! \n");
                return -1;
            }

        }while (!get_wifi_status(g_wlan_device->parent.netif));
(2)rtc 开始 ntp 同步自动时间,包括定期触发 同步时间   时间同步后,发送相关信号量给 时间处理线程,用于设置时间。其实不发送也可以,时间模块 只根据系统时间处理,不管是否已经同步时间。
static void ntp_sync_thread_enrty(void *param)
{
    extern time_t ntp_sync_to_rtc(const char *host_name);
    /* first sync delay for network connect */
    rt_thread_delay(RTC_NTP_FIRST_SYNC_DELAY * RT_TICK_PER_SECOND);

    while (1)
    {
        ntp_sync_to_rtc(NULL); //40天  定期的同步时间
        rt_thread_delay(RTC_NTP_SYNC_PERIOD * RT_TICK_PER_SECOND);
    }
}
(3) 通过mqtt  连接ucloud 后台,接收设置闹钟 时间  歌曲 及相关留言 。
接收到信息后,通过设置相关 留言信息及 闹钟值;
        关键代码1:针对接收的信息的解析处理如下:
/**
* MQTT消息接收处理函数
*
* @param topicName         topic主题
* @param topicNameLen      topic长度
* @param message           已订阅消息的结构
* @param userData         消息负载
*/
static void on_message_callback(void *pClient, MQTTMessage *message, void *userData) {
    if (message == NULL) {
        return;
    }  
    HAL_Printf("Receive Message With topicName:%.*s, payload:%.*s\n",
          (int) message->topic_len, message->topic, (int) message->payload_len, (char *) message->payload);

        /* 发送这个消息指针给 mq 消息队列 */
    rt_mq_send(&mq, (void*)&msg_ptr, sizeof(struct msg));
}
关键代码2: 通过消息队列的方式,将 mqtt 接收到的数据传输到 lcd 线程,并用 json 解析显示:
关键是 消息队列是直接的数据内容复制,所以在上面的例子中,都采用了局部变量的方式保存消息结构体,这样也就免去动态内存分配的烦恼了(也就不用担心,接收线程在接收到消息时,消息内存空间已经被释放)。
   while(1)
    {
     
    /* 从消息队列中接收消息到 msg_ptr 中 */
    if (rt_mq_recv(&mq, (void*)&msg_ptr, sizeof(struct msg), RT_WAITING_NO) == RT_EOK)//RT_WAITING_FOREVER
    {
        rt_kprintf("threadlcd: recv msg from msg queue, the content:%s\n", msg_ptr.data_ptr);
    //{"Hour":"5","Minute":"30","note":"just for fun"}
    object_json = json_get_object(JSOBJECT, (char *)msg_ptr.data_ptr);
    h = json_get_value_by_name(object_json, strlen(object_json),"Hour", &p_iValueLen, &p_iValueType);
    memcpy(hour_str,h,p_iValueLen);
    alarm_hour = atoi(hour_str);
    rt_kprintf("\n the ivalueLen is  %d : the ivalueType is %d the hour is %d \n",p_iValueLen, p_iValueType,alarm_hour);
    m = json_get_value_by_name(object_json, strlen(object_json),"Minute",&p_iValueLen, &p_iValueType);
    memcpy(minute,m, p_iValueLen);
    alarm_min = atoi(minute);
    rt_kprintf("\n the ivalueLen is  %d : the ivalueType is %d the minute is %d  \n",p_iValueLen, p_iValueType,alarm_min);
    s = json_get_value_by_name(object_json, strlen(object_json), "note", &p_iValueLen, &p_iValueType);
    memcpy(note,s,p_iValueLen);
    rt_kprintf("\n the note is %s \n", note);
    lcd_disp_str_at(20,20, note);



4. 时间处理模块
主要处理时间相关内容,具体逻辑如下:
(1) RTC  设备驱动使用  并开启NTP 时间自动同步
int rt_rtc_ntp_sync_init(void)
{
    static rt_bool_t init_ok = RT_FALSE;
    rt_thread_t thread;

    if (init_ok)
    {
        return 0;
    }

    thread = rt_thread_create("ntp_sync", ntp_sync_thread_enrty, RT_NULL, 1536, 26, 2);
    if (thread)
    {
        rt_thread_startup(thread);
    }
    else
    {
        return -RT_ENOMEM;
    }

    init_ok = RT_TRUE;
        
    return RT_EOK;
}
INIT_COMPONENT_EXPORT(rt_rtc_ntp_sync_init);
(2)倒计时模式 处理
      直接在五向按键线程中处理,显示也是在五向按键线程中,其中关键代码如下:
<span class="md-plain" style="box-sizing: border-box;" md-inline="plain">static void thread_entry(void *paramer)
{
    /* 按键检测线程中循环检测按键的状态并进行一定的消抖处理 */
    while(1)
    {
        if(rt_pin_read(KEY_UP) == PIN_LOW)
        {
            rt_thread_delay(120);
            if(rt_pin_read(KEY_UP) == PIN_LOW)
            {
                rt_kprintf(" key up is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    kitchen_timer_minute ++;
                }
            }
        }

        if(rt_pin_read(KEY_DOWN) == PIN_LOW)
        {
            rt_thread_delay(80);
            if(rt_pin_read(KEY_DOWN) == PIN_LOW)
            {
                rt_kprintf(" key down is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    kitchen_timer_minute --;
                }
            }
        }

         if(rt_pin_read(KEY_LEFT) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_LEFT) == PIN_LOW)
            {
                rt_kprintf(" key left is press ...\r\n");
                rt_kprintf("enter kitchen timer \n");
                flag_kitchen_timer = 1;
            }
        }

         if(rt_pin_read(KEY_RIGHT) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_RIGHT) == PIN_LOW)
            {
                rt_kprintf(" key right is press ...\r\n");
            
                if (flag_cycle_ear_mode > 0)
                {
                    rt_kprintf(" enter cycle ear mode %d\n",flag_cycle_ear_mode);
                    flag_cycle_ear_mode = 0;
                    play_mp3_stop();
                }
                else
                {
                  flag_cycle_ear_mode = 1;
                  //play_mp3_cycle_ear();                  /* code */
                  rt_event_send(&event_play_mp3, PLAYER_EVENT);
                }              

            }
        }

        if(rt_pin_read(KEY_MID) == PIN_LOW)
        {
            rt_thread_delay(100);
            if(rt_pin_read(KEY_MID) == PIN_LOW)
            {
                rt_kprintf(" key mid is press ...\r\n");
                if (flag_kitchen_timer == 1)
                {
                    rt_kprintf("the kitchen_timer_minute is %d\n", kitchen_timer_minute);
                    kitchen_timer_all_seconds  = kitchen_timer_minute * 60;
                    //lcd_clear(WHITE);
                   // lcd_set_color(WHITE, BLACK);
                    lcd_disp_str_at(20,120, "KITCHEN");  

                while (flag_kitchen_timer == 1)
                {
                    if (kitchen_timer_all_seconds == 0)
                    {
                        rt_kprintf(" time is up start play mp3  \n");
                        flag_kitchen_timer_mode = 1;
                        rt_event_send(&event_play_mp3, PLAYER_EVENT);
                        flag_kitchen_timer = 0;
                        kitchen_timer_minute = 0;
                        break;
                    }
                    lcd_display_kitchen_timer(kitchen_timer_all_seconds);
                    rt_thread_mdelay(1000);
                    kitchen_timer_all_seconds--;                                    
                    
                }
                }

               if(flag_cycle_ear_mode == 1)
               {
                   //暂停播放
                   pause_mp3();
                   flag_cycle_ear_mode ++;
               }
               else if (flag_cycle_ear_mode == 2)
               {
                   flag_cycle_ear_mode =1;
                   resume_play_mp3();
               }

   
            }
        }
         rt_thread_delay(80);

    }
}</span><span class="md-plain" style="box-sizing: border-box;" md-inline="plain">  </span>
5. 音频播放模块
主要是按定时事件处理音频数据。主要是根据 定时事件 或者 按键播放事件 来切换循环播放模式 或者 定时播放模式,主要内容如下:
(1)循环播放模式
   找到 下一首歌曲名字关键代码:
int find_next_song_path( char *uri)
{
   DIR *dirp;
    struct dirent *d;

    /* 打开 / dir_test 目录 */
    dirp = opendir("/sd/Music");
    if (dirp == RT_NULL)
    {
        rt_kprintf("open directory error!\n");
    }
    else
    {
        /* 读取目录 */
        while ((d = readdir(dirp)) != RT_NULL)
        {
            rt_kprintf("found %s\n", d->d_name);
                  rt_sprintf(uri,"/sd/mp3/%s",d->d_name);
            rt_kprintf("%s\n",uri);
            rt_kprintf("//////////////////////////// player_play \n");
            player_stop();
            player_set_uri(uri);
         
            player_play();
            dump_status();
            value = player_get_duration();//s
            rt_thread_mdelay(value*1000);//这个延时很重要 必须需要
            rt_kprintf("//////////////////////////// player_play end \n");
        }

        /* 关闭目录 */
        closedir(dirp);
    }   
}
(2)MP3 播放接收事件 处理
void mp3_player_process(void)
{
    rt_uint32_t e;
    char uri[256] = {0};
    while(1)
    {
        if(rt_event_recv(&event_play_mp3, (PLAYER_EVENT),
           RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
           RT_WAITING_FOREVER, &e) == RT_EOK)
           {
               rt_kprintf("mp3_player thread recv event 0x%x\n",e);
               while(flag_cycle_ear_mode == 1) //循环模式
               {
                   find_next_song_path(uri);
               }
           }
    }
}
有个问题:就是 停止以后 如何第二次再次进入循环播放模式。演示效果
功能1:开机 连接指定wifi  ,并同步 网络 时间,显示开机提示语 Have a Nice Day !
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/niceday.jpg?lastModify=1593829658

功能2: 连接上UCLOUD,发送命令:
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/cmd_mqtt.png?lastModify=1593829658
连接上
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/mqtt.png?lastModify=1593829658
功能3:厨房定时器模式
file:///D:/TimeManager_19nCov/BK7252Note/TimeManager_19nCov/doc/figure/kitchen.jpg?lastModify=1593829658
视频播放地址:
疫情小管家
代码地址
地址




视频播放地址: https://www.bilibili.com/video/BV1gV41167Pk/
小管家
code      


代码地址:
https://gitee.com/Gerryfan/TimeManager_19nCov

基于RT-Thread 使用麻雀 1号 DIY 疫情小管家.pdf

2.03 MB

使用特权

评论回复

相关帖子

沙发
电子禅石|  楼主 | 2020-7-4 10:35 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

6

帖子

0

粉丝