ESP32接入百度智能云语音识别,实现在线语音识别
0 - 总述
本文阐述一种使用 ESP32 + MAX9814麦克风模块,基于 ESP_Arudino 平台开发,通过百度智能云实现在线语音识别的系统。
1 - 准备工作
1-1 硬件
ESP32开发板

MAX9814麦克风模块

连接方式,请查阅代码头部的注释。
1-2 软件
1-2-1 Arduino 安装 ESPduino 环境
查阅代码头部包含的库,并安装好它们
1-3 百度智能云配置工作
1-3-1 注册账号并通过实名认证

最好为第一次注册使用,可享受免费赠送的功能包,一般为90天,过期后会收费。
1-3-2 创建语音识别应用
创建应用,接口建议全选

创建成功后,在应用列表中获取 API Key 和 Secret Key,保存起来,后续要写入到代码中。

在应用中按引导创建并上传你想要识别的词汇。
2- 代码与调试
2-1 使用 ESPArduino 创建工程
1 获取如下代码后,将 gain_token() 函数中的 API_Key 和 Secret_Key 替换为你在百度智能云中创建的应用的 Key,此时先不用管token,只要不会引发编译报错就好。确保 setup() 中调用 gain_token() 没有被注释,配置好你的WiFi,然后烧录,打开串口查看获取到 token,并保存下来;
2 将 token 填写到 loop() 中的位置,然后注释 setup() 中调用的 gain_token() ,再次烧录,打开串口,按下板载的 BOOT 按键,说出短语、词汇,再松开按键,查看串口是否有识别成功或不成功的打印,若有,恭喜你,基本路径已经调通了,接下来只需进行简单调试即可实现目的。
1 /* 2 基于ESP32开发板的百度语音识别功能实现 3 ADC采集录音版本 4 5 状态 | 未连接WiFi | 已连WiFi | 停止 |启动 6 D1 | 0 | 1 | 0 | 1 7 D2 | 0 | 0 | 1 | 1 8 **/ 9 #include <WiFi.h> 10 #include <WiFiMulti.h> 11 #include <HTTPClient.h> 12 #include <driver/i2s.h> 13 #include <base64.h> 14 #include <cJSON.h> 15 #include <ArduinoJson.h> 16 #include <Wire.h> 17 #include "SSD1306.h" 18 19 /* 20 * 注意事项 2023.03.16 更新 21 * token有效期约为90日,到期无效需重新获取新的token 22 * 百度智能云现行政策:新创建应用免费期限为90天,过期可能需要付费 23 * 代码中需要替换的参数:loop()中的token、gain_token()中的API Key、Secret Key 24 * 第一次烧录把API Key、Secret Key填好,token可以空着,在主函数中获取token,获取成功后再把获取token的操作注释掉,把token替换成你刚刚获取到的 25 * 智能云服务器收到有效的token才会处理,token无效或无token是得不到服务器的回复的 26 */ 27 28 29 #define key 0 // 召唤语音识别按键 30 #define led 2 // 蓝色指示灯 IO21 31 #define ADC 39 // ADC采集pin 32 #define D1 26 // 输出1 33 #define D2 27 // 输出2 34 35 WiFiMulti WiFiMulti; 36 HTTPClient http_client; 37 hw_timer_t * timer = NULL; // timer for voice collecting 38 39 // uint32_t chipId = 0; // chip id number 40 uint16_t adc_reading; 41 #define data_len 16000 42 uint16_t adc_data[data_len]; //16000个数据,8K采样率,即2分钟,录音时间为2分钟,想要实现更长时间的语音识别,就要改这个数组大小 43 //和下面data_json数组的大小,改大一些。 44 45 uint8_t adc_start_flag = 0; //开始标志 46 uint8_t adc_complete_flag = 0; //完成标志 47 48 char data_json[45000]; //用于储存json格式的数据,大一点,JSON编码后数据字节数变成原来的4/3,所以得计算好,避免出现越界 49 50 void IRAM_ATTR onTimer(); 51 void gain_token(void); 52 53 SSD1306 display(0x78, 21, 22); 54 55 void setup() { 56 // put your setup code here, to run once: 57 pinMode(ADC, ANALOG); 58 pinMode(key, INPUT_PULLUP); 59 pinMode(led, OUTPUT); 60 pinMode(D1, OUTPUT); 61 pinMode(D2, OUTPUT); 62 Serial.begin(115200); 63 digitalWrite(led, LOW); 64 digitalWrite(D1, LOW); 65 digitalWrite(D2, LOW); 66 delay(10); 67 68 69 70 digitalWrite(led, HIGH); 71 // We start by connecting to a WiFi network 72 WiFiMulti.addAP("HUAWE", "07Z33333"); 73 Serial.print("ESP32-- START INIT*******************"); 74 Serial.println(); 75 Serial.println(); 76 Serial.print("Waiting for WiFi... "); 77 78 uint8_t count = 0; 79 80 while (WiFiMulti.run() != WL_CONNECTED) 81 { 82 Serial.print("."); 83 count++; 84 if (count >= 75) 85 { 86 Serial.printf("\r\n-- wifi connect fail! --"); 87 break; 88 } 89 digitalWrite(led, LOW); 90 delay(500); 91 digitalWrite(led, HIGH); // lighting blue led 92 } 93 94 Serial.println(""); 95 Serial.println("WiFi connected"); 96 Serial.println("IP address: "); 97 Serial.println(WiFi.localIP()); 98 99 delay(500); 100 101 gain_token(); // 首次需要get, get到token之后则不必再次调用 102 103 timer = timerBegin(0, 80, true); // 80M的时钟 80分频 1M 104 timerAlarmWrite(timer, 125, true); // 1M 计125个数进中断 8K 105 timerAttachInterrupt(timer, &onTimer, true); 106 timerAlarmEnable(timer); 107 timerStop(timer); //先暂停 108 109 display.init(); 110 111 display.setFont(ArialMT_Plain_24); 112 display.drawString(0, 0, "--STBY--"); 113 display.display(); 114 115 digitalWrite(D1, HIGH); 116 digitalWrite(D2, LOW); 117 118 } 119 120 uint32_t time1, time2; 121 122 void loop() { 123 // put your main code here, to run repeatedly: 124 uint8_t count = 0; 125 digitalWrite(led, LOW); //shut blue light 126 if (digitalRead(key) == 0) //按键按下 127 { 128 while (WiFiMulti.run() != WL_CONNECTED) 129 { 130 digitalWrite(led, HIGH); //shut blue light 131 Serial.print("."); 132 count++; 133 if (count >= 75) 134 { 135 Serial.printf("\r\n-- wifi connect fail! --"); 136 break; 137 } 138 digitalWrite(led, LOW); 139 delay(500); 140 digitalWrite(led, HIGH); // lighting blue led 141 } 142 digitalWrite(led, LOW); //shut blue light 143 Serial.printf("Start recognition\r\n\r\n"); 144 //digitalWrite(led,HIGH); 145 adc_start_flag = 1; //数据开始采集 146 147 timerStart(timer); 148 while (!adc_complete_flag) //等待数据采集完成 149 { 150 ets_delay_us(10); 151 } 152 timerStop(timer); 153 adc_complete_flag = 0; //采集完成,清标志 154 155 //digitalWrite(led,LOW); 156 157 memset(data_json, '\0', strlen(data_json)); //将数组清空 158 strcat(data_json, "{"); 159 strcat(data_json, "\"format\":\"pcm\","); 160 strcat(data_json, "\"rate\":8000,"); //采样率 如果采样率改变了,记得修改该值,只有16000、8000两个固定采样率 161 strcat(data_json, "\"dev_pid\":1537,"); //中文普通话 162 strcat(data_json, "\"channel\":1,"); //单声道 163 strcat(data_json, "\"cuid\":\"123456\","); //识别码 随便打几个字符,但最好唯一 164 strcat(data_json, "\"token\":\"your_token\","); //token your_token这里需要修改成自己申请到的token
//strcat(data_json, "\"token\":\"24.edf*****************************************************35-25137363\","); //token 这里需要修改成自己申请到的token 165 strcat(data_json, "\"len\":32000,"); //数据长度 如果传输的数据长度改变了,记得修改该值,该值是ADC采集的数据字节数,不是base64编码后的长度 166 strcat(data_json, "\"speech\":\""); 167 strcat(data_json, base64::encode((uint8_t *)adc_data, sizeof(adc_data)).c_str()); //base64编码数据 这里使用的base64编码的库,在base.h头文件中 168 strcat(data_json, "\""); 169 strcat(data_json, "}"); 170 171 int httpCode; 172 http_client.begin("http://vop.baidu.com/server_api"); //请求API 173 http_client.addHeader("Content-Type", "application/json"); //设置固定头部:Content-Type:application/json 174 httpCode = http_client.POST(data_json); //POST请求 175 176 if (httpCode > 0) 177 { 178 if (httpCode == HTTP_CODE_OK) 179 { 180 String payload = http_client.getString(); //接收数据 181 DynamicJsonDocument jsonBuffer(600); 182 deserializeJson(jsonBuffer, payload); 183 JsonObject root = jsonBuffer.as<JsonObject>(); 184 long corpus_no = root["corpus_no"]; 185 String result = root["result"]; 186 187 Serial.println(payload); 188 Serial.println(result); 189 Serial.println(corpus_no); 190 if(result == "[\"启动。\"]") 191 { 192 display.drawString(0, 0, "--WORK--"); 193 display.display(); 194 Serial.println("D1=1,D2=1"); 195 digitalWrite(D1, HIGH); 196 digitalWrite(D2, HIGH); 197 } 198 else if(result == "[\"停止。\"]") 199 { 200 display.drawString(0, 0, "--STOP--"); 201 display.display(); 202 Serial.println("D1=0,D2=1"); 203 digitalWrite(D1, LOW); 204 digitalWrite(D2, HIGH); 205 } 206 else 207 { 208 display.drawString(0, 0, "--NONE--"); 209 Serial.println("D1=1,D2=0"); 210 digitalWrite(D1, HIGH); 211 digitalWrite(D2, LOW); 212 } 213 214 #if 0 215 if(payload == "{\"corpus_no\":\"7031184841660747698\",\"err_msg\":\"success.\",\"err_no\":0,\"result\":[\"启动。\"],\"sn\":\"757702466251637075292\"}") 216 { 217 Serial.println("D1=1,D2=1"); 218 digitalWrite(D1, HIGH); 219 digitalWrite(D2, HIGH); 220 } 221 else if(payload == "{\"corpus_no\":\"7031184875815380182\",\"err_msg\":\"success.\",\"err_no\":0,\"result\":[\"停止。\"],\"sn\":\"494182777151637075300\"}") 222 { 223 Serial.println("D1=0,D2=1"); 224 digitalWrite(D1, LOW); 225 digitalWrite(D2, HIGH); 226 } 227 else 228 { 229 Serial.println("D1=1,D2=0"); 230 digitalWrite(D1, HIGH); 231 digitalWrite(D2, LOW); 232 } 233 #endif 234 } 235 } 236 else 237 { 238 Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str()); 239 } 240 http_client.end(); 241 242 while (!digitalRead(key)); 243 Serial.printf("Recognition complete\r\n"); 244 } 245 246 247 } 248 249 250 uint32_t num = 0; 251 portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 252 253 void IRAM_ATTR onTimer() 254 { 255 // Increment the counter and set the time of ISR 256 portENTER_CRITICAL_ISR(&timerMux); 257 if (adc_start_flag == 1) 258 { 259 adc_data[num] = analogRead(ADC); 260 num++; 261 if (num >= data_len) 262 { 263 adc_complete_flag = 1; 264 adc_start_flag = 0; 265 num = 0; 266 Serial.println("Voice collected"); 267 } 268 } 269 portEXIT_CRITICAL_ISR(&timerMux); 270 } 271 272 273 /* 获取 token */ 274 void gain_token(void) //获取token 275 { 276 int httpCode; 277 //注意,要把下面网址中的<your_apikey>和<your_secretkey>替换成在百度智能云创建的应用的API Key和Secret Key 278 http_client.begin("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=<your_apikey>&client_secret=<your_secretkey>");
//http_client.begin("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=H******************4Vsv&client_secret=Tr*********************fE");
280 httpCode = http_client.GET(); 281 if (httpCode > 0) { 282 if (httpCode == HTTP_CODE_OK) { 283 String payload = http_client.getString(); 284 Serial.println(payload); 285 } 286 } 287 else { 288 Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str()); 289 } 290 http_client.end(); 291 }

浙公网安备 33010602011771号