使用inmp441和esp32进行讯飞在线语音识别 要求使用wsbsocketsclient通信方式编写完整代码 要求代码在VSCOde platformio中运行 并且不要使用esp32的psram
时间: 2025-08-05 07:14:31 浏览: 11
<think>由于我们无法使用PSRAM,因此需要注意内存使用。同时,Inmp441是I2S麦克风,ESP32有I2S接口。我们将使用WiFiClientSecure与讯飞的WebSocket服务器进行通信。讯飞的语音识别服务需要按照其协议发送数据,包括握手、音频数据发送和结束标记。步骤:1.连接WiFi。2.初始化I2S麦克风(INMP441)。3.获取讯飞语音识别的WebSocketURL,该URL需要包含鉴权参数(一般通过APIKey和Secret生成,或者直接使用带有有效期的URL)。4.使用WebSocket客户端(我们使用WebSocketsClient库)连接到讯飞服务器。5.按照讯飞协议发送音频数据(格式为RAW,即PCM数据),同时接收识别结果。6.处理识别结果并打印。注意:讯飞语音识别WebSocket协议的详细步骤:-建立WebSocket连接。-发送握手消息(一个JSON,包含appid、apiKey、timestamp等,以及一个signature,signature由apiKey、timestamp和secret通过hmac-sha256生成)。-开始发送音频数据(每帧音频数据放入一个WebSocket帧中,采用binary类型)。-发送结束标记(一个空的binary帧,标记音频结束)。由于内存限制,我们可能需要分块读取I2S数据并发送。我们将使用以下库(在PlatformIO中):-WiFiClientSecure:用于底层的SSL连接。-WebSocketsClient:用于WebSocket通信。在PlatformIO的配置文件中,需要包含以下依赖:[env:esp32dev]platform=espressif32board=esp32devframework=arduinolib_deps=links2004/WebSockets@^2.3.6arduino-libraries/WiFi@^1.0讯飞语音识别的WebSocket地址为:wss://ws-api.xfyun.cn/v2/iat由于我们需要生成带签名的URL,因此需要先生成timestamp和signature。生成signature的步骤:1.获取当前时间戳(以秒为单位)。2.拼接签名原文:apiKey+timestamp+secret(这里apiKey和secret在讯飞控制台获取)。3.对原文进行SHA-256哈希(HMAC方式),然后base64编码。然后,我们将这些参数放在URL的查询字符串中:wss://ws-api.xfyun.cn/v2/iat?appid=YOUR_APPID&signa=生成的签名&ts=时间戳注意:讯飞要求每次请求都要重新生成签名。此外,在握手消息中还需要发送一个JSON,包括:"common":{"app_id":"YOUR_APPID"},"business":{"language":"zh_cn",//中文"domain":"iat",//领域"accent":"mandarin"//普通话},"data":{"status":0,//0表示开始,1表示继续,2表示结束"format":"audio/L16;rate=16000",//音频格式,16K采样率"encoding":"raw",//原始PCM"audio":""//第一帧为空,后面发送音频数据}但是,注意讯飞WebSocket协议要求:第一帧:发送握手消息(文本帧),然后开始发送音频数据(二进制帧),最后发送一个结束帧(空二进制帧)。然而,讯飞也支持在一开始不发送握手消息,而是将握手消息作为第一个文本帧发送,然后紧接着发送音频二进制帧。具体可以参考讯飞文档。由于内存限制,我们可能无法存储整个音频,所以需要边录边发。我们将设置一个采样率16kHz,单声道,16位样本。INMP441是单声道麦克风。硬件连接:INMP441的3V或5V接ESP32的3.3V,GND接GND。LRCL接GPIO25(或任意,这里我们使用25作为WS引脚)。DOUT接GPIO26(数据输出)。BCLK接GPIO27(位时钟)。以下代码将实现:1.连接WiFi。2.设置I2S麦克风(使用默认的16位,16kHz,单声道)。3.生成签名和WebSocketURL。4.通过WebSocket连接并发送音频,同时接收识别结果。注意:讯飞要求音频数据是16位、16kHz、单声道、小端序。由于不使用PSRAM,所以要注意I2S缓冲区大小,不能太大。我们将从I2S读取数据后直接发送,不进行额外存储。代码结构:-包含必要的库。-设置WiFi账号密码。-定义I2S引脚。-定义讯飞接口参数(appid,apiKey,secret)。-设置采样参数(16000Hz,16位,单声道)。由于讯飞WebSocket服务需要HTTPS,所以需要根证书。我们可以使用WiFiClientSecure并设置根证书(讯飞的根证书可以从其官网获取,或者使用全局的CA证书)。但是,为了简化,我们可以暂时不验证服务器证书(这样不安全,但测试用):client.setInsecure();//不验证服务器证书由于WebSocketsClient库的使用,我们需要在连接时设置服务器地址和端口(443)以及URL。步骤:1.初始化I2S。2.连接WiFi。3.生成时间戳和签名,构造URL。4.初始化WebSocket连接,注册事件回调(处理连接、数据接收、断开等)。5.开始录音并发送音频数据,直到检测到静音(或者设定一个录音时长),然后发送结束帧。6.等待识别结果并打印。由于讯飞要求每个WebSocket连接只能处理一句话,所以识别结束后我们需要关闭连接。我们使用一个标志位来控制录音和发送。注意:在发送音频数据之前,我们需要先发送握手消息(文本帧)。参考讯飞文档,第一帧必须是文本帧,内容如下(JSON格式):{"common":{...},"business":{...},"data":{...}}其中,data部分的第一帧(文本帧)的data.status为0(表示第一帧),然后在发送音频数据时,每帧数据都放在一个单独的二进制帧中,在最后一帧音频数据之后,发送一个空的二进制帧(长度为0)表示结束。然而,讯飞文档指出:也可以在第一帧文本帧之后,开始发送音频数据,并在音频数据发送完毕后发送结束标记(空二进制帧)。但是,注意在文本帧中,我们设置data.status=0表示开始,而中间数据帧在发送时不需要再发送文本帧,只需要发送二进制帧。在结束时,发送一个空二进制帧(或者一个状态为2的文本帧?)。这里需要参考讯飞的最新文档。根据讯飞在线语音识别WebSocket文档(v2.1):-第一帧:发送文本帧(包含握手信息,其中data.status=0,表示开始)-第二帧开始:发送二进制帧(音频数据)-最后:发送一个空二进制帧(0字节)表示音频数据发送完毕。因此,我们在代码中按照这个顺序发送。由于代码较长,下面将分步骤编写。注意:因为内存有限,我们每次从I2S读取少量数据(例如640字节)并立即发送。我们将使用状态机:0:初始状态1:已连接WebSocket,发送握手帧(文本帧)2:已发送握手帧,开始发送音频数据3:检测到结束(静音或超时),发送结束帧(空二进制帧)4:等待结果在回调函数中处理接收到的识别结果。以下代码示例:</think>
以下是一个完整的代码实现,使用ESP32和INMP441麦克风通过WebSocket与讯飞语音识别服务通信。代码针对PlatformIO环境设计,不使用PSRAM。
```cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <WebSocketsClient.h>
#include <driver/i2s.h>
#include <base64.h>
#include "time.h"
// Wi-Fi配置
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// 讯飞配置 (需替换为你的认证信息)
#define APPID "YOUR_APPID"
#define API_KEY "YOUR_API_KEY"
#define API_SECRET "YOUR_API_SECRET"
const char* websocket_url = "ws://ws-api.xfyun.cn/v2/iat";
// I2S配置
#define I2S_PORT I2S_NUM_0
#define SAMPLE_RATE 16000
#define BUFFER_SIZE 512
#define PIN_CLK 25
#define PIN_DATA 26
// WebSocket客户端
WebSocketsClient webSocket;
WiFiClientSecure client;
// 时间服务
const char* ntpServer = "pool.ntp.org";
// 生成签名
String generateSignature(uint64_t timestamp) {
String base = "host: ws-api.xfyun.cn\n";
base += "date: " + String(timestamp) + "\n";
base += "GET /v2/iat HTTP/1.1";
uint8_t hash[32];
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init(&ctx);
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1);
mbedtls_md_hmac_starts(&ctx, (const uint8_t*)API_SECRET, strlen(API_SECRET));
mbedtls_md_hmac_update(&ctx, (const uint8_t*)base.c_str(), base.length());
mbedtls_md_hmac_finish(&ctx, hash);
mbedtls_md_free(&ctx);
return base64::encode(hash, 32);
}
// WebSocket事件处理
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.println("WebSocket disconnected");
break;
case WStype_CONNECTED:
Serial.println("WebSocket connected");
break;
case WStype_TEXT: {
String response = String((char*)payload, length);
Serial.println("Received response:");
Serial.println(response);
break;
}
case WStype_BIN:
Serial.println("Received binary data");
break;
}
}
// 初始化I2S麦克风
void initI2S() {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 4,
.dma_buf_len = BUFFER_SIZE,
};
i2s_pin_config_t pin_config = {
.bck_io_num = PIN_CLK,
.ws_io_num = PIN_CLK,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = PIN_DATA
};
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL);
i2s_set_pin(I2S_PORT, &pin_config);
}
// 设置时间服务
void setDateTime() {
configTime(8 * 3600, 0, ntpServer);
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
delay(500);
now = time(nullptr);
}
}
void setup() {
Serial.begin(115200);
// 连接Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
setDateTime();
initI2S();
// 生成认证参数
time_t now = time(nullptr);
String signature = generateSignature(now);
// 连接WebSocket
webSocket.beginSSL("ws-api.xfyun.cn", 80);
webSocket.setAuthorization("api_key", API_KEY);
webSocket.addHeader("host: ws-api.xfyun.cn");
webSocket.addHeader("date: " + String(now));
webSocket.addHeader("authorization: api_key=\"" + String(API_KEY) +
"\", algorithm=\"hmac-sha256\", headers=\"host date\", signature=\"" + signature + "\"");
webSocket.onEvent(webSocketEvent);
}
// 音频录制和发送
void recordAndSend() {
int16_t i2s_buffer[BUFFER_SIZE];
size_t bytes_read = 0;
while (true) {
i2s_read(I2S_PORT, &i2s_buffer, BUFFER_SIZE * sizeof(int16_t), &bytes_read, portMAX_DELAY);
// 转换为单声道PCM
int16_t mono_buffer[BUFFER_SIZE / 2];
for (int i = 0; i < bytes_read / 4; i++) {
mono_buffer[i] = i2s_buffer[i * 2];
}
// 发送音频数据
if (webSocket.isConnected()) {
webSocket.sendBIN((uint8_t*)mono_buffer, bytes_read / 2);
}
// 添加静音检测逻辑 (需根据实际环境调整)
// if (isSilent(mono_buffer, bytes_read/4)) break;
}
// 发送结束标记
const char end_msg[] = "{\"end\": true}";
webSocket.sendTXT(end_msg);
}
void loop() {
webSocket.loop();
if (webSocket.isConnected()) {
// 发送开始消息
const char start_msg[] = "{\"common\":{\"app_id\":\"" APPID "\"},\"business\":{\"language\":\"zh_cn\"},\"data\":{\"status\":0}}";
webSocket.sendTXT(start_msg);
// 录制和发送音频
recordAndSend();
// 等待结果
delay(5000);
}
delay(100);
}
```
### 代码说明:
1. **硬件设置**
- INMP441通过I2S接口连接ESP32
- CLK引脚:GPIO25
- DATA引脚:GPIO26
2. **工作流程**
- 初始化Wi-Fi连接
- 通过NTP获取当前时间(用于签名)
- 初始化I2S麦克风(16kHz采样率,16位深度)
- 生成HMAC-SHA256签名
- 连接讯飞WebSocket服务
- 录制音频并通过WebSocket发送
- 接收识别结果
3. **关键组件**
- **WebSocket通信**:使用WebSocketsClient库
- **音频采集**:使用ESP32的I2S接口
- **安全认证**:通过API Key和动态签名
- **时间服务**:NTP同步时间用于签名
4. **平台依赖**
- 需要在platformio.ini中添加依赖:
```
lib_deps =
links2004/WebSockets@^2.3.6
vshymanskyy/base64@^0.4.0
```
5. **注意事项**
- 替换代码中的`YOUR_WIFI_SSID`、`YOUR_WIFI_PASSWORD`等占位符
- 在实际应用中需添加静音检测(VAD)
- 讯飞API限制:单次识别时长有限制(需查文档)
- 不支持回放功能(仅上传音频)
### 调试建议:
1. 使用示波器验证I2S信号
2. 通过串口查看认证过程
3. 用Wireshark抓包检查WebSocket帧
4. 先使用预录制的音频测试
阅读全文
相关推荐




















