作者 | 哈猪猪
前言
在文章2中,我们简单分析了 HTTPClient 组件的源码,发现其并不能实现“multipart/form-data”类型内容的 POST 请求。因此在本文中,我们将手动实现这一功能,并且最终完成整个云语音识别项目。以下为全部开发流程:
项目源码:https://github.com/ha-zhuzhu/ch2601_speech_recognizer
实现效果:请见演示视频
RVB2601
首先,我们结合之前所作的工作,重新梳理云语音识别项目的目标与思路。
目标:按下板载按钮,RVB2601 开始录音,上传云端语音识别后返回识别结果至串口。
思路:实现的程序流程与所需要的关键组件如下:
- gpio_pin:设置按键中断。按下按钮后设置“开始录音标志位”为1。
- rhino:注册监控任务。监控“开始录音标志位”,若为1则:
-
- codec:进行录音。
- HTTPClient :上传录音数据,接收并返回识别结果。
- 清空标志位
HTTPClient 发送录音数据
首先从最复杂的,手动发送音频数据开始。
借鉴另一个组件 http 的思路,先针对我们的应用场景设置好 boundary 和请求体的头部、尾部:
// 请求体格式
static const char *boundary = "----WebKitFormBoundarypNjgoVtFRlzPquKE";
#define MY_FORMAT_START "------WebKitFormBoundarypNjgoVtFRlzPquKE\r\nContent-Disposition: %s; name=\"%s\"; filename=\"%s\"\r\nContent-Type: %s\r\n\r\n"
#define MY_FORMAT_END "\r\n------WebKitFormBoundarypNjgoVtFRlzPquKE--\r\n"
由于我们的音频数据较大,且一次流程只发送一个文件,因此发送 POST 请求体时可以分三部分发送:头部、音频数据、尾部。所以在进行音频数据传输时,相比于原来的流程,我们要先设置“Content-Type”,接着基于模板制备请求体头部尾部,设置 Content-Length,并将 POST 相关变量统一存放在一个结构体中,方便进行 POST 请求时调用。以下是音频发送函数:
static void post_audio()
{
http_client_config_t config = {
.url = "http://upload.hazhuzhu.com/myasr.php",
.method = HTTP_METHOD_POST,
.event_handler = _http_event_handler,
};
http_client_handle_t client = http_client_init(&config);
// 设置 Content-Type
http_client_set_header(client, "Content-Type", "multipart/form-data; boundary=----WebKitFormBoundarypNjgoVtFRlzPquKE");
const unsigned char *post_content = repeater_data_addr; // POST 请求体的内容为录音数据
// 请求体格式相关变量
const char *content_disposition = "form-data";
const char *name = "file";
const char *filename = "raw_recording";
const char *content_type = "application/octet-stream";
int boundary_len = strlen(boundary);
// 制备请求体头部
int post_start_len = strlen(MY_FORMAT_START) - 8 + strlen(content_disposition) + strlen(name) + strlen(filename) + strlen(content_type) + 1;
char *post_start = (char *)malloc(post_start_len + 1);
memset(post_start, 0, sizeof(post_start_len));
snprintf(post_start, post_start_len, MY_FORMAT_START, content_disposition, name, filename, content_type);
const char *post_end = MY_FORMA