基于RTL8722DMMINI实现云语音识别开发

原创 面包板社区 2022-02-28 17:58

前言


Ameba RTL8722DM MINI 板载功能非常丰富,支持 Wifi,蓝牙,Codec,麦克风耳机孔,Micro SD卡接口等……并且还有丰富的外围接口可以连接各种传感器。因此我们能够通过这块套件实现绝大部分物联网场景下的应用,如云语音识别。


Realtek 官网上提供了 RTL8722DM MINI 详细资料,并给出了针对其不同功能的各种 ARDUINO 示例程序,数量多达七十多个,而且各种库写的非常简洁、源码注释清晰,因此开发起来非常舒适。


简单浏览,找到几个与本测评项目相关的库:

  • HttpClient:支持http协议一些比较基本的操作。

  • FatFs_SD:读写 Fat 文件格式的 SD 卡。

  • RecordWav:能直接录制 wav 格式音频到 SD 卡上,也可以播放。


因此,云语音识别的实现流程为:

  • 按键检测,利用 RecordWav 录制音频到 SD 卡。

  • 利用 FatFs_SD 读取刚录制的音频。

  • 利用 HttpClient 将音频 POST 到服务器,服务器调用语音识别 api 后将识别结果返回给 Ameba。最后返回流程1。


开发流程

Ameba

HttpClient

HttpClient 支持 HTTP 的各种请求,不过功能还不够完善。目前该库还不支持持久连接,如 post() 源码的主要调用流程为:

  • startRequest():



    • sendInitialHeaders():发送请求头信息,包括“Connection: close”


    • finishHeaders():发送空行结束 header


  1. /* HttpClient.cpp */

  2. // We don't support persistent connections, so tell the server to

  3. // close this connection after we're done

  4. sendHeader(HTTP_HEADER_CONNECTION, "close");

这样 POST 在发送请求头信息后就会结束连接,因此我们需要作一定的拓展。


POST 二进制流类型的文件对 POST 请求头和请求体的格式具有一定要求,我们只需要按照要求的格式和流程发送数据即可。由于我们一次流程只发送一个语音文件,并且其体积较大,因此请求体中只有头部及尾部的两个 boundary,以及中间的音频内容。因此我们可以提前设置好两个 boundary,以及 Content_Type, Content-Length 等内容以制作请求头,并在请求体中分三个部分发送即可:

  1. /* speech_recognizer.ino */

  2. char Content_Type[] = "multipart/form-data; boundary=----WebKitFormBoundarypNjgoVtFRlzPquKE";

  3. // 请求体头部及尾部数据

  4. char post_start[] = "------WebKitFormBoundarypNjgoVtFRlzPquKE\r\nContent-Disposition: form-data; name="file"; filename="ameba_recording"\r\nContent-Type: application/octet-stream\r\n\r\n";

  5. char post_end[] = "\r\n------WebKitFormBoundarypNjgoVtFRlzPquKE--\r\n";

  6. const int post_start_len = strlen(post_start);

  7. const int post_end_len = strlen(post_end);

在 setup() 中将它们写入我们在 HttpClient 中添加的成员变量中。在本项目中需要一边读取 sd 卡一边发送,因此不提前设置 post_content。

  1. /* speech_recognizer.ino */

  2. void setup()

  3. {

  4.     uint8_t *post_content = NULL;

  5.     http.mysetPostData(post_start, post_end, post_content, post_start_len, post_end_len, 0, Content_Type);

  6. }


  7. /* HttpClient.cpp */

  8. int HttpClient::mysetPostData(char *post_start_d, char *post_end_d, uint8_t *post_content_d, int post_start_len_d,

  9.                               int post_end_len_d, int post_content_len_d, char *content_type_d)

  10. {

  11.     post_start = post_start_d;

  12.     post_end = post_end_d;

  13.     post_content = post_content_d;

  14.     post_start_len = post_start_len_d;

  15.     post_end_len = post_end_len_d;

  16.     post_content_len = post_content_len_d;

  17.     post_len = post_start_len_d + post_content_len_d + post_end_len_d;

  18.     content_type = content_type_d;

  19. }

每次录音成功后再读取文件,更新内容大小相关变量:

  1. /* HttpClient.cpp */

  2. int HttpClient::mysetPostContent(uint8_t *post_content_d, int post_content_len_d, int post_len_d)

  3. {

  4.     post_content = post_content_d;

  5.     post_content_len = post_content_len_d;

  6.     post_len = post_len_d;

  7. }

最后添加方法 mypost(),流程:


  • mystartRequest():


    • mysendInitialHeaders():发送请求头信息。

    • finishHeaders():发送空行结束 header。


  • 发送请求体头部,一边读取音频一边发送,发送请求体尾部。



实现:


  1. /* HttpClient.h */

  2. #define HTTP_CONTENT_TYPE "Content-Type"


  3. /* HttpClient.cpp */

  4. int HttpClient::mysendInitialHeaders(const char *aServerName, IPAddress aServerIP, uint16_t aPort, const char *aURLPath, const char *aHttpMethod, const char *aUserAgent)

  5. {

  6. // 与 sendInitialHeaders() 主要区别部分

  7. sendHeader(HTTP_HEADER_CONNECTION, "keep-alive");

  8. sendHeader(HTTP_HEADER_CONTENT_LENGTH, post_len);

  9. sendHeader(HTTP_CONTENT_TYPE, content_type);

  10. }/* HttpClient.cpp */

  11. int HttpClient::mypost(const char *aServerName, const char *aURLPath, SdFatFile file, const char *aUserAgent)

  12. {

  13.     const int MY_BODY_SIZE = 1000;

  14.     uint8_t buf_temp[MY_BODY_SIZE]; // 读取音频并发送的缓冲

  15.     memset(buf_temp, 0, MY_BODY_SIZE);


  16.     // 发送请求头

  17.     int req_ret = mystartRequest(aServerName, kHttpPort, aURLPath, HTTP_METHOD_POST, aUserAgent);

  18.     if (HTTP_SUCCESS != req_ret)

  19.     {

  20.         return req_ret;

  21.     }


  22.     // 发送请求体头部

  23.     iClient->write((const uint8_t *)post_start, post_start_len);


  24.     // 发送音频文件,可能分多次发送

  25.     int read_bytes = file.read(buf_temp, MY_BODY_SIZE);

  26.     iClient->write(buf_temp, read_bytes);

  27.     while (read_bytes == MY_BODY_SIZE)

  28.     {

  29.         read_bytes = file.read(buf_temp, MY_BODY_SIZE);

  30.         iClient->write(buf_temp, read_bytes);

  31.     }


  32.     // 发送请求体尾部

  33.     iClient->write((const uint8_t *)post_end, post_end_len);


  34.     return HTTP_SUCCESS;

  35. }



FatFs_SD

功能完善可以直接使用。貌似只支持Fat32?需要提前制备 SD 卡。


没有获取文件大小的方法,自己实现:


  1. /* SdFatFile.cpp */

  2. int SdFatFile::size() {

  3.     return f_size((FIL *)m_file);

  4. }



RecordWav

功能完善,可以直接录制生成 Wav 格式的音频文件,不需要手动给 PCM 文件写文件头,能直接发给云端进行语音识别,非常方便。只需要提前设置采样率位深等信息。


主循环

实现了各种方法后就能在主循环内实现云语音识别的流程了:

  1. void loop()

  2. {

  3.     if ((digitalRead(RECORDBTN) == HIGH) && (!recWav.fileOpened()))

  4.     {

  5.         // 按下按钮录制音频

  6.         sprintf(record_file_name, "%d.wav", record_counter);

  7.         sprintf(absolute_filename, "%s%s", fs.getRootPath(), record_file_name);

  8.         Serial.println("Recording started");

  9.         recWav.openFile(absolute_filename);

  10.     }

  11.     else if ((digitalRead(RECORDBTN) == LOW) && (recWav.fileOpened()))

  12.     {   

  13.         // 松开按钮停止录制

  14.         Serial.println("Recording stopped");

  15.         recWav.closeFile();

  16.         

  17.         // 文件保存后再打开文件,获取大小

  18.         SdFatFile record_file = fs.open(absolute_filename);

  19.         int record_file_len = record_file.size();

  20.         printf("size:%d", record_file_len);

  21.         

  22.         // 设置 Post Content 相关变量

  23.         http.mysetPostContent(NULL, record_file_len, post_start_len + record_file_len + post_end_len);

  24.         // POST 音频数据

  25.         err = http.mypost(kHostname, kPath, record_file);

  26.         // 关闭文件句柄

  27.         record_file.close();

  28.         

  29.         // 读取解析 response (省略)

  30.         if (err == 0){...}

  31.         delay(100);

  32. }

服务器

服务器端搭建在我的个人网站上,nginx+PHP架构,使用腾讯云提供的语音识别 api。假设板端将请求提交到 http://asr.hazhuzhu.com/ameba_asr.php


nginx

  1. server

  2. {

  3.     listen 80;

  4.     server_name asr.hazhuzhu.com;


  5.     client_max_body_size 128m;


  6.     root /home/wwwroot/asr;

  7.     index index.html index.htm index.php;


  8.     location / {

  9.         try_files $uri $uri/ =404;

  10.     }


  11.     location ~ \.php$ {

  12.         include fastcgi.conf;

  13.         fastcgi_pass    unix:/tmp/php-cgi.sock;

  14.         fastcgi_keep_conn on;

  15.     }

  16. }


PHP

后端负责存储音频文件并调用语音识别 api(需要使用腾讯云相关 SDK),最后返回识别结果。api 调用部分腾讯云提供了相关文档和代码生成工具,比较方便。

  1. $uploads_dir = 'ameba_recordings';


  2. if ($_FILES['file']['error'] == UPLOAD_ERR_OK)

  3. {

  4.         $tmp_name = $_FILES['file']['tmp_name'];

  5.         // $name = $_FILES['file']['name'];

  6.         $date_str=date('YmdHis');

  7.         move_uploaded_file($tmp_name, "$uploads_dir/$date_str".'.wav');

  8.         

  9.          try {

  10.          // 调用 api 部分,省略

  11.                 $result_str=$resp->getResult();

  12.                 $result_file=fopen("$uploads_dir/$date_str".'.txt',"a");

  13.                 fwrite($result_file,$result_str);

  14.                 fclose($result_file);

  15.                 echo $result_str;

  16.         }

  17.         catch(TencentCloudSDKException $e) {

  18.                 echo $e;

  19.         }

至此我们完成了一个完整的基于 Ameba RTL8722DM MINI 的云语音识别应用。


总结

首先我想谈谈使用 Ameba RTL8722DM MINI 的开发体验。正如前文所述,MINI 体积小但功能强大,能够实现大部分物联网场景应用。并且 MINI 支持 Arduino 开发,很快就能上手实现,不需要构建繁杂的单片机工程。而且官网的文档非常详尽,大量的示例程序、api文档、硬件细节以及详细的源码注释等,极大地方便了我们的开发。相关工作人员也很有耐心,点赞!


其次,其它开发者有提到麦克风的问题。我感觉确实录制下来的声音比较小,可能是缺省增益不够大?不过最终实现的语音识别效果还不错,没有什么影响。


最后再次感谢 Realtek 与面包板平台提供的这次测评与学习的机会。囿于时间和技术水平,本项目还有许多值得改进的地方,包括代码规范与识别效率等,希望能和大家共同交流学习!也祝主办方的技术生态越来越好!


项目源码:https://github.com/ha-zhuzhu/RTL8722DM-MINI-speech_recognizer

项目演示视频:点击阅读原文即可观看。

面包板社区 面包板社区——中国第一电子人社交平台 面包板社区是Aspencore旗下媒体,整合了电子工程专辑、电子技术设计、国际电子商情丰富资源。社区包括论坛、博客、问答,拥有超过250万注册用户,加入面包板社区,从菜鸟变大神,打造您的电子人脉社交圈!
评论
  • 《高速PCB设计经验规则应用实践》+PCB绘制学习与验证读书首先看目录,我感兴趣的是这一节;作者在书中列举了一条经典规则,然后进行详细分析,通过公式推导图表列举说明了传统的这一规则是受到电容加工特点影响的,在使用了MLCC陶瓷电容后这一条规则已经不再实用了。图书还列举了高速PCB设计需要的专业工具和仿真软件,当然由于篇幅所限,只是介绍了一点点设计步骤;我最感兴趣的部分还是元件布局的经验规则,在这里列举如下:在这里,演示一下,我根据书本知识进行电机驱动的布局:这也算知行合一吧。对于布局书中有一句:
    wuyu2009 2024-11-30 20:30 141浏览
  • 作为优秀工程师的你,已身经百战、阅板无数!请先醒醒,新的项目来了,这是一个既要、又要、还要的产品需求,ARM核心板中一个处理器怎么能实现这么丰富的外围接口?踌躇之际,你偶阅此文。于是,“潘多拉”的魔盒打开了!没错,USB资源就是你打开新世界得钥匙,它能做哪些扩展呢?1.1  USB扩网口通用ARM处理器大多带两路网口,如果项目中有多路网路接口的需求,一般会选择在主板外部加交换机/路由器。当然,出于成本考虑,也可以将Switch芯片集成到ARM核心板或底板上,如KSZ9897、
    万象奥科 2024-12-03 10:24 86浏览
  • 11-29学习笔记11-29学习笔记习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习笔记&记录学习习笔记&记学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&记录学习学习笔记&学习学习笔记&记录学习学习笔记&记录学习学习笔记&记
    youyeye 2024-12-02 23:58 90浏览
  • TOF多区传感器: ND06   ND06是一款微型多区高集成度ToF测距传感器,其支持24个区域(6 x 4)同步测距,测距范围远达5m,具有测距范围广、精度高、测距稳定等特点。适用于投影仪的无感自动对焦和梯形校正、AIoT、手势识别、智能面板和智能灯具等多种场景。                 如果用ND06进行手势识别,只需要经过三个步骤: 第一步&
    esad0 2024-12-04 11:20 94浏览
  • 当前,智能汽车产业迎来重大变局,随着人工智能、5G、大数据等新一代信息技术的迅猛发展,智能网联汽车正呈现强劲发展势头。11月26日,在2024紫光展锐全球合作伙伴大会汽车电子生态论坛上,紫光展锐与上汽海外出行联合发布搭载紫光展锐A7870的上汽海外MG量产车型,并发布A7710系列UWB数字钥匙解决方案平台,可应用于数字钥匙、活体检测、脚踢雷达、自动泊车等多种智能汽车场景。 联合发布量产车型,推动汽车智能化出海紫光展锐与上汽海外出行达成战略合作,联合发布搭载紫光展锐A7870的量产车型
    紫光展锐 2024-12-03 11:38 121浏览
  • 最近几年,新能源汽车愈发受到消费者的青睐,其销量也是一路走高。据中汽协公布的数据显示,2024年10月,新能源汽车产销分别完成146.3万辆和143万辆,同比分别增长48%和49.6%。而结合各家新能源车企所公布的销量数据来看,比亚迪再度夺得了销冠宝座,其10月新能源汽车销量达到了502657辆,同比增长66.53%。众所周知,比亚迪是新能源汽车领域的重要参与者,其一举一动向来为外界所关注。日前,比亚迪汽车旗下品牌方程豹汽车推出了新车方程豹豹8,该款车型一上市就迅速吸引了消费者的目光,成为SUV
    刘旷 2024-12-02 09:32 131浏览
  • 戴上XR眼镜去“追龙”是种什么体验?2024年11月30日,由上海自然博物馆(上海科技馆分馆)与三湘印象联合出品、三湘印象旗下观印象艺术发展有限公司(下简称“观印象”)承制的《又见恐龙》XR嘉年华在上海自然博物馆重磅开幕。该体验项目将于12月1日正式对公众开放,持续至2025年3月30日。双向奔赴,恐龙IP撞上元宇宙不久前,上海市经济和信息化委员会等部门联合印发了《上海市超高清视听产业发展行动方案》,特别提到“支持博物馆、主题乐园等场所推动超高清视听技术应用,丰富线下文旅消费体验”。作为上海自然
    电子与消费 2024-11-30 22:03 103浏览
  • 光伏逆变器是一种高效的能量转换设备,它能够将光伏太阳能板(PV)产生的不稳定的直流电压转换成与市电频率同步的交流电。这种转换后的电能不仅可以回馈至商用输电网络,还能供独立电网系统使用。光伏逆变器在商业光伏储能电站和家庭独立储能系统等应用领域中得到了广泛的应用。光耦合器,以其高速信号传输、出色的共模抑制比以及单向信号传输和光电隔离的特性,在光伏逆变器中扮演着至关重要的角色。它确保了系统的安全隔离、干扰的有效隔离以及通信信号的精准传输。光耦合器的使用不仅提高了系统的稳定性和安全性,而且由于其低功耗的
    晶台光耦 2024-12-02 10:40 134浏览
  • 遇到部分串口工具不支持1500000波特率,这时候就需要进行修改,本文以触觉智能RK3562开发板修改系统波特率为115200为例,介绍瑞芯微方案主板Linux修改系统串口波特率教程。温馨提示:瑞芯微方案主板/开发板串口波特率只支持115200或1500000。修改Loader打印波特率查看对应芯片的MINIALL.ini确定要修改的bin文件#查看对应芯片的MINIALL.ini cat rkbin/RKBOOT/RK3562MINIALL.ini修改uart baudrate参数修改以下目
    Industio_触觉智能 2024-12-03 11:28 104浏览
  • 概述 说明(三)探讨的是比较器一般带有滞回(Hysteresis)功能,为了解决输入信号转换速率不够的问题。前文还提到,即便使能滞回(Hysteresis)功能,还是无法解决SiPM读出测试系统需要解决的问题。本文在说明(三)的基础上,继续探讨为SiPM读出测试系统寻求合适的模拟脉冲检出方案。前四代SiPM使用的高速比较器指标缺陷 由于前端模拟信号属于典型的指数脉冲,所以下降沿转换速率(Slew Rate)过慢,导致比较器检出出现不必要的问题。尽管比较器可以使能滞回(Hysteresis)模块功
    coyoo 2024-12-03 12:20 156浏览
  • RDDI-DAP错误通常与调试接口相关,特别是在使用CMSIS-DAP协议进行嵌入式系统开发时。以下是一些可能的原因和解决方法: 1. 硬件连接问题:     检查调试器(如ST-Link)与目标板之间的连接是否牢固。     确保所有必要的引脚都已正确连接,没有松动或短路。 2. 电源问题:     确保目标板和调试器都有足够的电源供应。     检查电源电压是否符合目标板的规格要求。 3. 固件问题: &n
    丙丁先生 2024-12-01 17:37 111浏览
  •         温度传感器的精度受哪些因素影响,要先看所用的温度传感器输出哪种信号,不同信号输出的温度传感器影响精度的因素也不同。        现在常用的温度传感器输出信号有以下几种:电阻信号、电流信号、电压信号、数字信号等。以输出电阻信号的温度传感器为例,还细分为正温度系数温度传感器和负温度系数温度传感器,常用的铂电阻PT100/1000温度传感器就是正温度系数,就是说随着温度的升高,输出的电阻值会增大。对于输出
    锦正茂科技 2024-12-03 11:50 138浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦