基于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万注册用户,加入面包板社区,从菜鸟变大神,打造您的电子人脉社交圈!
评论
  • 日前,商务部等部门办公厅印发《手机、平板、智能手表(手环)购新补贴实施方案》明确,个人消费者购买手机、平板、智能手表(手环)3类数码产品(单件销售价格不超过6000元),可享受购新补贴。每人每类可补贴1件,每件补贴比例为减去生产、流通环节及移动运营商所有优惠后最终销售价格的15%,每件最高不超过500元。目前,京东已经做好了承接手机、平板等数码产品国补优惠的落地准备工作,未来随着各省市关于手机、平板等品类的国补开启,京东将第一时间率先上线,满足消费者的换新升级需求。为保障国补的真实有效发放,基于
    华尔街科技眼 2025-01-17 10:44 221浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 122浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 153浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 55浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 115浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 102浏览
  • 随着消费者对汽车驾乘体验的要求不断攀升,汽车照明系统作为确保道路安全、提升驾驶体验以及实现车辆与环境交互的重要组成,日益受到业界的高度重视。近日,2024 DVN(上海)国际汽车照明研讨会圆满落幕。作为照明与传感创新的全球领导者,艾迈斯欧司朗受邀参与主题演讲,并现场展示了其多项前沿技术。本届研讨会汇聚来自全球各地400余名汽车、照明、光源及Tier 2供应商的专业人士及专家共聚一堂。在研讨会第一环节中,艾迈斯欧司朗系统解决方案工程副总裁 Joachim Reill以深厚的专业素养,主持该环节多位
    艾迈斯欧司朗 2025-01-16 20:51 198浏览
  •  光伏及击穿,都可视之为 复合的逆过程,但是,复合、光伏与击穿,不单是进程的方向相反,偏置状态也不一样,复合的工况,是正偏,光伏是零偏,击穿与漂移则是反偏,光伏的能源是外来的,而击穿消耗的是结区自身和电源的能量,漂移的载流子是 客席载流子,须借外延层才能引入,客席载流子 不受反偏PN结的空乏区阻碍,能漂不能漂,只取决于反偏PN结是否处于外延层的「射程」范围,而穿通的成因,则是因耗尽层的过度扩张,致使跟 端子、外延层或其他空乏区 碰触,当耗尽层融通,耐压 (反向阻断能力) 即告彻底丧失,
    MrCU204 2025-01-17 11:30 182浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 46浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 412浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 186浏览
  • 本文介绍瑞芯微开发板/主板Android配置APK默认开启性能模式方法,开启性能模式后,APK的CPU使用优先级会有所提高。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。源码修改修改源码根目录下文件device/rockchip/rk3562/package_performance.xml并添加以下内容,注意"+"号为添加内容,"com.tencent.mm"为AP
    Industio_触觉智能 2025-01-17 14:09 164浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 73浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦