RT-Thread实战笔记|语音控制获取心知天气信息

2021-09-10 16:32:02 浏览数 (1)

点击上方「嵌入式实验基地」,选择「置顶公众号」第一时间查看嵌入式笔记!

前言

上篇文章介绍了LD3320的用法,小飞哥“灵光一现”,何不做一个语音控制获取天气信息的东东,既然想了,那就干它,来,淦~小飞哥前面说过rt-thread好用,那今天一起来看看到底有多好用...

演示效果

硬件环境

ART-PI(其他开发板也可以)

LD3320语音模块

板载wifi(ESP8266也可以)

“一张会说话的嘴”

软件环境

RT-Thread studio 版本2.1.1

  • 软件包

webclient V2.1.2

mbedtls V2.7.10.1

cjson V1.7.14

netutils V1.3.1

fal V0.5.0

本次的知识点还是相当的丰富的,搬好小板凳,泡杯茶,坐一会慢慢看吧...

wifi配置

在LD3320的工程基础之上,来添加wifi,ART-PI上板载的wifi模组是AP6212,是蓝牙与wifi一体的模组,本次我们只使用wifi功能

在setting勾选即可,对应的宏为ART_PI_USING_WIFI_6212_LIB,勾选之后,工程会自动添加相关宏定义控制的代码

完了,CTRL S保存到我们的工程就可以了...好像啥也没干,来看看效果是怎样的

在控制台可以看到,很多关于网络的配置已经初始化完成了,终端输入wifi,可以看到几个关于wifi的指令,本次用到的是wifi的AP功能,连接家里的wifi

输入wifi scan,来看看附近都有哪些“hot pot”,还是有几个的,也不隐藏了,欢迎过来蹭网~

使用wifi join命令加入网络即可,千万不要跟着教程输入我家的...

wifi join "你家热点名称" "你家无线密码"

网络状况好像不太好,还重连了,顺便把重连功能验证了,连接成功之后,可以看到是自动获取IP地址的,输入ifconfig,来看看获取到的地址相关的,DHCP功能是使能的

接下来,开发板“点灯”,网络经典ping百度...还算比较顺利

就这样,我们的wifi就完成了,对,就这样...一行代码没写,sun le dog,好怀念写代码的日子

这样直接添加wifi功能的方式,掉电是不会保存wifi信息的,需要重新wifi join

https访问组件配置

webclient 软件包

setting->Add->输入webclient->确认找到对应软件包

双击打开可以详细看到关于此软件包的用法

-点击软件包,进入详细配置界面,

  • Enable webclient GET/POST samples :添加示例代码;
  • Select TLS mode :配置开启 HTTPS 支持,选择支持的模式
  • Not support:不支持 TLS 功能
  • SAL TLS support:配置 SAL 组件中 TLS 功能支持,SAL 组件中抽象 TLS 操作,用户还需要手动配置开启使用的 TLS 软件包类型(目前只支持 MbedTLS 软件包)
  • MbedTLS support:配置 MbedTLS 功能支持
  • Version :配置软件包版本

mbedtls软件包

使能mbedtls功能之后,mbedtls软件包自动添加,mbedTLS(前身 PolarSSL)是一个由 ARM 公司开源和维护的 SSL/TLS 算法库。其使用 C 编程语言以最小的编码占用空间实现了 SSL/TLS 功能及各种加密算法,易于理解、使用、集成和扩展,方便开发人员轻松地在嵌入式产品中使用 SSL/TLS 功能

详细配置

  • Select Root Certificate ---> # 选择证书文件
  • mbedtls: An portable and flexible SSL/TLS library # 打开 mbedtls 软件包
  • Store the AES tables in ROM # 将 AES 表存储在 ROM 中
  • Maximum window size used # 用于点乘的最大“窗口”大小(2-7) (3584) Maxium fragment length in bytes # 配置数据帧大小
  • Enable a mbedtls client example # 开启 mbedtls 测试例程
  • Enable Debug log output # 开启调试 log 输出
  • version (latest) ---> # 选择软件包版本,默认为最新版本

注意:

在调试的时候,获取不同的信息时,有时候返回的数据量太大,报错-0x7200

不要慌,百度错误码,可能是数据buffer太小了,增大buffer即可解决

关于证书的内容就比较多了,小伙伴可以自行百度或者查看mbedtls软件包的使用教程

netutils

这里汇集了 RT-Thread 可用的全部网络小工具集合,你所需要的小工具都可以在这里找到。

TLS 握手是证书验证需要时间的验证,添加此网络工具用于时间同步

同时要开启RTC功能,否则会报错

RTC详细配置界面

至此,网络功能就配置完成了,接下来跑一下例程,看下效果

web_get我们是获取到一串字符串,看看源码地址什么

浏览器输入此地址,获取到的正是这些字符

TLS例程比较麻烦点,要先进行SSL/TLS 认证,才能连接,这就牵扯到HTTP和HTTPS的区别了,篇幅太大,不易展开...

同样的,看下源地址是什么内容

到这里,我们的HTTPS访问就建立完成了,还没有写一句代码...

心知天气API

大家需要注册一个心知天气账号,注册完之后使用免费的即可

天气API

https://api.seniverse.com/v3/weather/now.json?key=“你的秘钥”&location=“地点”

我们点击一个进去看看,我查看的是北京的天气,数据是json格式的,我们只需要解析json包就可以啦

原始数据

这里教大家一个小技巧,如何查看网址的IP地址,端口号,只需要在网页按F12进入调试界面,宣导网络,重新加载

代码编写

新建文件,或者在上面例程里面修改,首先把网址改为心知天气的API接口

成功获取到天气数据

json解析

代码语言:javascript复制
static int cJSON_NowWeatherParse(char *JSON)
{
    cJSON *json,*arrayItem,*object,*subobject,*item;
    int size;
    json = cJSON_Parse(JSON);//解析JSON数据包
        if(json == NULL)          //检测JSON数据包是否存在语法上的错误,返回NULL表示数据包无效
        {
            rt_kprintf("Error before: [%s]rn",cJSON_GetErrorPtr()); //打印数据包语法错误的位置
            return 1;
        }
        else
        {
            if((arrayItem = cJSON_GetObjectItem(json,"results")) != NULL); //匹配字符串"results",获取数组内容
            {
                size = cJSON_GetArraySize(arrayItem);     //获取数组中对象个数
                rt_kprintf("cJSON_GetArraySize: size=%dn",size);

                if((object = cJSON_GetArrayItem(arrayItem,0)) != NULL)//获取父对象内容
                {
                    rt_kprintf("--------------------------------Now Weather Data Begin------------------------------rn");
                    /* 匹配子对象1 */
                    if((subobject = cJSON_GetObjectItem(object,"location")) != NULL)
                    {

                        rt_kprintf("---------------------------------subobject1-------------------------------rn");
                        if((item = cJSON_GetObjectItem(subobject,"id")) != NULL)
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                        if((item = cJSON_GetObjectItem(subobject,"name")) != NULL) //地名--需要用到的数据
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                        if((item = cJSON_GetObjectItem(subobject,"country")) != NULL)
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                        if((item = cJSON_GetObjectItem(subobject,"path")) != NULL)
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                        if((item = cJSON_GetObjectItem(subobject,"timezone")) != NULL)
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                        if((item = cJSON_GetObjectItem(subobject,"timezone_offset")) != NULL)
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);
                        }
                    }
                    /* 匹配子对象2 */
                    if((subobject = cJSON_GetObjectItem(object,"now")) != NULL)
                    {
                        rt_kprintf("---------------------------------subobject2-------------------------------rn");
                        if((item = cJSON_GetObjectItem(subobject,"text")) != NULL)//天气预报文字--需要用到的数据
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string,item->valuestring);

                        }
                        if((item = cJSON_GetObjectItem(subobject,"code")) != NULL)//天气预报代码--需要用到的数据
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string, item->valuestring);

                        }
                        if((item = cJSON_GetObjectItem(subobject,"temperature")) != NULL) //温度--需要用到的数据
                        {
                            rt_kprintf("cJSON_GetObjectItem: %s:%srn",item->string, item->valuestring);
                        }
                    }
                    /* 匹配子对象3 */
                    if((subobject = cJSON_GetObjectItem(object,"last_update")) != NULL)
                    {
                        rt_kprintf("---------------------------------subobject3-------------------------------rn");
                        rt_kprintf("cJSON_GetObjectItem: %s:%srn",subobject->string,subobject->valuestring);
                    }
                    rt_kprintf("--------------------------------Now Weather Data End--------------------------------rn");
                }
            }
        }
        cJSON_Delete(json); //释放cJSON_Parse()分配出来的内存空间
        return 0;
}

解析后的数据:

接下来,结合上节我们做的LD3320,实现语音控制获取不同地点的天气信息:

先来定义几个地点:

代码语言:javascript复制
static char *REQUEST[4] ={

        "GET /v3/weather/now.json?key=SYahiZcoJ587MEA3R&location=beijing HTTP/1.1rn"
            "Host: api.seniverse.comrn"
            //"User-Agent: rtthread/3.1 rttrn"
            "rn",

        "GET /v3/weather/now.json?key=SYahiZcoJ587MEA3R&location=suzhou HTTP/1.1rn"
            "Host: api.seniverse.comrn"
            //"User-Agent: rtthread/3.1 rttrn"
            "rn",

        "GET /v3/weather/now.json?key=SYahiZcoJ587MEA3R&location=shanghai HTTP/1.1rn"
            "Host: api.seniverse.comrn"
            //"User-Agent: rtthread/3.1 rttrn"
            "rn",

        "GET /v3/weather/now.json?key=SYahiZcoJ587MEA3R&location=shenzhen HTTP/1.1rn"
            "Host: api.seniverse.comrn"
            //"User-Agent: rtthread/3.1 rttrn"
            "rn",
};

然后在语音中添加相应的识别信息:

代码语言:javascript复制
//Identification code (Customer Modification Office)

#define CODE_RUN 1   //commond code for LED waterfall
#define CODE_KEY 2   //commond code for LED flash
#define CODE_FLASH 3 //commond code for LED reversal
#define CODE_PLAY 4  //commond code for PLAY MP3
#define CODE_BEIJING_Weather 5              //北京
#define CODE_SUZHOU_Weather 6               //苏州
#define CODE_SHANGHAI_Weather 7             //上海
#define CODE_SHENZHEN_Weather 8             //深圳


    rt_uint8_t sRecog[DATE_A][DATE_B] = {
        //add commond,use pinying
        "ni hao",
        "ni lai zi na li",
        "bei yi shou shi",
        "shi jie zhe me da",
        "bei jing tian qi",
        "shang hai tian qi",
        "su zhou tian qi",
        "shen zhen tian qi"
    };
    rt_uint8_t pCode[DATE_A] = {
        //add commond code to do the commond
        CODE_RUN,
        CODE_KEY,
        CODE_FLASH,
        CODE_PLAY,
        CODE_BEIJING_Weather,
        CODE_SHANGHAI_Weather,
        CODE_SUZHOU_Weather,
        CODE_SHENZHEN_Weather
    };

函数执行:

代码语言:javascript复制
rt_uint8_t status =0;
void rt_Board_text(rt_uint8_t Code_Val)
{
    switch (Code_Val)
    {
    case CODE_RUN: //Commond "liu shui deng"
        rt_LED_run();
        break;
    case CODE_KEY: //Commond "an jian"
        rt_LED_key();
        break;
    case CODE_FLASH: //Commond "shan shuo"
        rt_LED_flash();
        break;
    case CODE_PLAY: //Commond "bo fang"
        rt_Play_demo();
        break;
    case CODE_BEIJING_Weather:
        status=0;
        Weather();
        break;
    case CODE_SUZHOU_Weather:
        status=1;
        Weather();
        break;
    case CODE_SHANGHAI_Weather:
        status=2;
        Weather();
        break;
    case CODE_SHENZHEN_Weather:
        status=3;
        Weather();
        break;
    default:
        break;
    }
}

然后改一下例程里面的请求函数,根据识别到的语音置相应的状态,调用不同的API:

代码语言:javascript复制
  while ((ret = mbedtls_client_write(tls_session, (const unsigned char *) REQUEST[status], rt_strlen(REQUEST[status]))) <= 0)
    {
        if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
        {
            rt_kprintf("mbedtls_ssl_write returned -0x%xn", -ret);
            goto __exit;
        }
    }

资料获取

关注公众号,后台“天气”,即可获得本教程源码,欢迎添加小飞哥好友,进群一起交流

0 人点赞