ESP32接入百度智能云语音识别,实现在线语音识别

0 - 总述

本文阐述一种使用 ESP32 + MAX9814麦克风模块,基于 ESP_Arudino 平台开发,通过百度智能云实现在线语音识别的系统。

 

1 - 准备工作

1-1 硬件

ESP32开发板

 

MAX9814麦克风模块

 

连接方式,请查阅代码头部的注释。

 

1-2 软件

1-2-1 Arduino 安装 ESPduino 环境

 查阅代码头部包含的库,并安装好它们

 

1-3 百度智能云配置工作

1-3-1 注册账号并通过实名认证

百度智能云-智能时代基础设施 (baidu.com)

 

 

最好为第一次注册使用,可享受免费赠送的功能包,一般为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 }

 

posted @ 2023-02-19 23:14  大咖喝咖  阅读(29)  评论(0)    收藏  举报