視頻地址:
ESP32S3 Sense接入百度在線語音識別
目前這是我使用的ESP32S3官方硬件??????(小小的身材有大大的力量)只需要35元加攝像頭麥克風79元,后期我會整理相關專欄進行Arduino系統(tǒng)學習??????。有需要可以購買xiao開發(fā)板??????,SeeedXIAO ESP32S3 Sense硬件購買地址:https://s.click.taobao.com/lekazrt
1. 前言
使用Seeed XIAO ESP32S3 Sense開發(fā)板接入百度智能云實現(xiàn)在線語音識別。自帶麥克風模塊用做語音輸入,通過串口發(fā)送字符“1”來控制數(shù)據(jù)的采集和上傳。
步驟概括 ??
(1) 在百度云控制端選擇“語音識別”并創(chuàng)建應用獲取API Key和Secret Key獲取token ??
(2)采集音頻數(shù)據(jù),將數(shù)據(jù)打包成規(guī)定的格式,POST發(fā)送到請求API
(3) 接收返回的識別數(shù)據(jù)
2. 操作流程
2.1 創(chuàng)建語音識別應用
登錄百度云賬號,選擇語音識別
??官網地址:https://ai.baidu.com/tech/speech
新用戶可以直接領取資源,也可付費接入,創(chuàng)建應用。
根據(jù)創(chuàng)建應用生成的API Key和Secret Key來獲取token,創(chuàng)建好應用,點管理應用,會有API Key和Secret Key,如下圖應用創(chuàng)建成功
2.2 API秘鑰創(chuàng)建
點擊在線調試
按照如下順序選擇
有了API Key和Secret Key就可以得到token,下面附上ESP32進行get請求得到token的代碼
access_token對應的值就是可用的token了,每次申請的token有效期為30天,過期需要重新申請,可以申請多個。不用每次都調用獲取token的程序,申請一個可以用30天,定時更新就可以吧。
3. JSON語音接入
采集數(shù)據(jù),POST發(fā)送到請求API數(shù)據(jù)上傳 POST 方式有 2 種:JSON 格式和RAW 格式。
3.1 JSON格式
這里介紹使用使用JSON格式上傳的方式,下圖為JSON格式上傳的一些必要的參數(shù)說明
3.2 ESP32S3 Sense接入代碼
圖中對數(shù)據(jù)類型和內容說的很明確了,只需要按照這個格式打包好數(shù)據(jù)然后發(fā)送就行,下面是ESP32的具體實現(xiàn)代碼。
#include <Arduino.h>
#include "base64.h"
#include "WiFi.h"
#include "HTTPClient.h"
#include "cJSON.h"
#include <I2S.h>
#include <ArduinoJson.h>
// #define key 4 //端口0
// #define ADC 2 //端口39
// #define led 15 //端口2
const int buttonPin = 1; // the number of the pushbutton pin
const int ledPin = 21; // the number of the LED pin
HTTPClient http_client;
// 1. Replace with your network credentials
const char* ssid = "J09 502";
const char* password = "qwertyuiop111";
hw_timer_t* timer = NULL;
#define data_len 16000
uint16_t adc_data[data_len]; //16000個數(shù)據(jù),8K采樣率,即2秒,錄音時間為2秒,想要實現(xiàn)更長時間的語音識別,就要改這個數(shù)組大小
//和下面data_json數(shù)組的大小,改大一些。
uint8_t adc_start_flag = 0; //開始標志
uint8_t adc_complete_flag = 0; //完成標志
char data_json[45000]; //用于儲存json格式的數(shù)據(jù),大一點,JSON編碼后數(shù)據(jù)字節(jié)數(shù)變成原來的4/3,所以得計算好,避免出現(xiàn)越界
void IRAM_ATTR onTimer();
void gain_token(void);
void setup() {
//Serial.begin(921600);
Serial.begin(115200);
// pinMode(ADC, ANALOG);
// pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
// start I2S at 16 kHz with 16-bits per sample
I2S.setAllPins(-1, 42, 41, -1, -1);
if (!I2S.begin(PDM_MONO_MODE, 16000, 16)) {
Serial.println("Failed to initialize I2S!");
while (1)
; // do nothing
}
uint8_t count = 0;
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
count++;
if (count >= 75) {
Serial.printf("\r\n-- wifi connect fail! --");
break;
}
vTaskDelay(200);
}
Serial.printf("\r\n-- wifi connect success! --\r\n");
// gain_token();
timer = timerBegin(0, 80, true); // 80M的時鐘 80分頻 1M
timerAlarmWrite(timer, 125, true); // 1M 計125個數(shù)進中斷 8K
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmEnable(timer);
timerStop(timer); //先暫停
}
uint32_t time1, time2;
void loop() {
if (Serial.available() > 0) //按鍵按下
{
if (Serial.read() == '1') {
Serial.printf("Start recognition\r\n\r\n");
digitalWrite(ledPin, HIGH);
adc_start_flag = 1;
timerStart(timer);
// time1=micros();
while (!adc_complete_flag) //等待采集完成
{
ets_delay_us(10);
}
// time2=micros()-time1;
timerStop(timer);
adc_complete_flag = 0; //清標志
digitalWrite(ledPin, LOW);
// Serial.printf("time:%d\r\n",time2); //打印花費時間
memset(data_json, '\0', strlen(data_json)); //將數(shù)組清空
strcat(data_json, "{");
strcat(data_json, "\"format\":\"pcm\",");
strcat(data_json, "\"rate\":16000,"); //采樣率 如果采樣率改變了,記得修改該值,只有16000、8000兩個固定采樣率
strcat(data_json, "\"dev_pid\":1537,"); //中文普通話
strcat(data_json, "\"channel\":1,"); //單聲道
strcat(data_json, "\"cuid\":\"666666\","); //識別碼 隨便打幾個字符,但最好唯一
strcat(data_json, "\"token\":\"24.8f6133335e191.2592000.1713789066.282335-57722200\","); //token 這里需要修改成自己申請到的token
strcat(data_json, "\"len\":32000,"); //數(shù)據(jù)長度 如果傳輸?shù)臄?shù)據(jù)長度改變了,記得修改該值,該值是ADC采集的數(shù)據(jù)字節(jié)數(shù),不是base64編碼后的長度
strcat(data_json, "\"speech\":\"");
strcat(data_json, base64::encode((uint8_t*)adc_data, sizeof(adc_data)).c_str()); //base64編碼數(shù)據(jù)
strcat(data_json, "\"");
strcat(data_json, "}");
// Serial.println(data_json);
int httpCode;
http_client.setTimeout(5000);
http_client.begin("http://vop.baidu.com/server_api"); //https://vop.baidu.com/pro_api
http_client.addHeader("Content-Type", "application/json");
httpCode = http_client.POST(data_json);
if (httpCode == 200) {
if (httpCode == HTTP_CODE_OK) {
String response = http_client.getString();
http_client.end();
Serial.println(response);
// Parse JSON response
DynamicJsonDocument jsonDoc(512);
deserializeJson(jsonDoc, response);
String outputText = jsonDoc["result"][0];
// 訪問"result"數(shù)組,并獲取其第一個元
// 輸出結果
Serial.println(outputText);
} else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str());
}
}
// while (!digitalRead(buttonPin))
// ;
Serial.printf("Recognition complete\r\n");
}
}
vTaskDelay(1);
}
uint32_t num = 0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer() {
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
if (adc_start_flag == 1) {
//Serial.println("");
// adc_data[num] = analogRead(ADC);
adc_data[num] = I2S.read();
num++;
if (num >= data_len) {
adc_complete_flag = 1;
adc_start_flag = 0;
num = 0;
//Serial.println(Complete_flag);
}
}
portEXIT_CRITICAL_ISR(&timerMux);
}
// void gain_token(void) //獲取token
// {
// int httpCode;
// //注意,要把下面網址中的your_apikey和your_secretkey替換成自己的API Key和Secret Key
// http_client.begin("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=your_apikey&client_secret=your_secretkey");
// httpCode = http_client.GET();
// if (httpCode > 0) {
// if (httpCode == HTTP_CODE_OK) {
// String payload = http_client.getString();
// Serial.println(payload);
// }
// } else {
// Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str());
// }
// http_client.end();
// }
3.3 ESP32接入代碼
使用ESP32接入百度智能云實現(xiàn)在線語音識別,max9814麥克風模塊用做語音輸入,一個按鍵來控制數(shù)據(jù)的采集和上傳
#include <Arduino.h>
#include "base64.h"
#include "WiFi.h"
#include "HTTPClient.h"
#include "cJSON.h"
#define key 4 //端口0
#define ADC 2 //端口39
#define led 15 //端口2
HTTPClient http_client;
hw_timer_t * timer = NULL;
#define data_len 16000
uint16_t adc_data[data_len]; //16000個數(shù)據(jù),8K采樣率,即2秒,錄音時間為2秒,想要實現(xiàn)更長時間的語音識別,就要改這個數(shù)組大小
//和下面data_json數(shù)組的大小,改大一些。
uint8_t adc_start_flag=0; //開始標志
uint8_t adc_complete_flag=0; //完成標志
char data_json[45000]; //用于儲存json格式的數(shù)據(jù),大一點,JSON編碼后數(shù)據(jù)字節(jié)數(shù)變成原來的4/3,所以得計算好,避免出現(xiàn)越界
void IRAM_ATTR onTimer();
void gain_token(void);
void setup() {
//Serial.begin(921600);
Serial.begin(115200);
pinMode(ADC,ANALOG);
pinMode(key,INPUT_PULLUP);
pinMode(led,OUTPUT);
uint8_t count=0;
WiFi.disconnect(true);
WiFi.begin("1111110","88888888");//填寫自己的wifi賬號密碼
while (WiFi.status() != WL_CONNECTED)
{
Serial.print(".");
count++;
if(count>=75){
Serial.printf("\r\n-- wifi connect fail! --");
break;
}
vTaskDelay(200);
}
Serial.printf("\r\n-- wifi connect success! --\r\n");
// gain_token();
timer = timerBegin(0, 80, true); // 80M的時鐘 80分頻 1M
timerAlarmWrite(timer, 125, true); // 1M 計125個數(shù)進中斷 8K
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmEnable(timer);
timerStop(timer); //先暫停
}
uint32_t time1,time2;
void loop() {
if(digitalRead(key)==0) //按鍵按下
{
Serial.printf("Start recognition\r\n\r\n");
digitalWrite(led,HIGH);
adc_start_flag=1;
timerStart(timer);
// time1=micros();
while(!adc_complete_flag) //等待采集完成
{
ets_delay_us(10);
}
// time2=micros()-time1;
timerStop(timer);
adc_complete_flag=0; //清標志
digitalWrite(led,LOW);
// Serial.printf("time:%d\r\n",time2); //打印花費時間
memset(data_json,'\0',strlen(data_json)); //將數(shù)組清空
strcat(data_json,"{");
strcat(data_json,"\"format\":\"pcm\",");
strcat(data_json,"\"rate\":8000,"); //采樣率 如果采樣率改變了,記得修改該值,只有16000、8000兩個固定采樣率
strcat(data_json,"\"dev_pid\":1537,"); //中文普通話
strcat(data_json,"\"channel\":1,"); //單聲道
strcat(data_json,"\"cuid\":\"666666\","); //識別碼 隨便打幾個字符,但最好唯一
strcat(data_json,"\"token\":\"24.c640024cd1355e\","); //token 這里需要修改成自己申請到的token
strcat(data_json,"\"len\":32000,"); //數(shù)據(jù)長度 如果傳輸?shù)臄?shù)據(jù)長度改變了,記得修改該值,該值是ADC采集的數(shù)據(jù)字節(jié)數(shù),不是base64編碼后的長度
strcat(data_json,"\"speech\":\"");
strcat(data_json,base64::encode((uint8_t *)adc_data,sizeof(adc_data)).c_str()); //base64編碼數(shù)據(jù)
strcat(data_json,"\"");
strcat(data_json,"}");
// Serial.println(data_json);
int httpCode;
http_client.begin("http://vop.baidu.com/server_api"); //https://vop.baidu.com/pro_api
http_client.addHeader("Content-Type","application/json");
httpCode = http_client.POST(data_json);
if(httpCode == 200) {
if(httpCode == HTTP_CODE_OK) {
String payload = http_client.getString();
Serial.println(payload);
}
}
else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str());
}
http_client.end();
while (!digitalRead(key));
Serial.printf("Recognition complete\r\n");
}
}
uint32_t num=0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer(){
// Increment the counter and set the time of ISR
portENTER_CRITICAL_ISR(&timerMux);
if(adc_start_flag==1)
{
//Serial.println("");
adc_data[num]=analogRead(ADC);
num++;
if(num>=data_len)
{
adc_complete_flag=1;
adc_start_flag=0;
num=0;
//Serial.println(Complete_flag);
}
}
portEXIT_CRITICAL_ISR(&timerMux);
}
void gain_token(void) //獲取token
{
int httpCode;
//注意,要把下面網址中的your_apikey和your_secretkey替換成自己的API Key和Secret Key
http_client.begin("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=your_apikey&client_secret=your_secretkey");
httpCode = http_client.GET();
if(httpCode > 0) {
if(httpCode == HTTP_CODE_OK) {
String payload = http_client.getString();
Serial.println(payload);
}
}
else {
Serial.printf("[HTTP] GET... failed, error: %s\n", http_client.errorToString(httpCode).c_str());
}
http_client.end();
}
上面代碼是將數(shù)據(jù)拼接成要求的JSON的格式并通過POST的方式發(fā)送到請求API,并接收打印返回的數(shù)據(jù)消息。使用的定時器設置成8K頻率定時采集音頻數(shù)據(jù),上面代碼中并未展示,后面會附上完整代碼。
??ESP32是有JSON庫的,在 “cJSON.h” 頭文件中,但是我沒有用,因為我發(fā)現(xiàn)數(shù)據(jù)太長時不知道為啥會出現(xiàn)莫名其妙的錯誤,也沒深究,就使用函數(shù)strcat()將數(shù)據(jù)拼接成規(guī)定的格式,好使,就是寫的時候麻煩一些,但問題不大。
??POST發(fā)送數(shù)據(jù)有一個固定頭部:Content-Type:application/json,POST前需要設置一下。
4. 接收數(shù)據(jù)
參考以下燒錄配置,
串口輸入字符“1”,點擊按回車鍵,然后有2s錄音時間。等待百度在線語音識別返回,在上一步的代碼中實現(xiàn)了接收數(shù)據(jù),這里列一下返回的數(shù)據(jù)。
22:04:58.854 -> Start recognition
22:04:58.854 ->
22:05:01.558 -> {"corpus_no":"7349559668823131804","err_msg":"success.","err_no":0,"result":["你好。"],"sn":"922395388061711202708"}
22:05:01.558 ->
22:05:01.558 -> 你好。
22:05:01.558 -> Recognition complete
22:08:46.838 -> Start recognition
22:08:46.838 ->
22:08:49.809 -> {"corpus_no":"7349560648200206506","err_msg":"success.","err_no":0,"result":["你知道百度嗎?"],"sn":"497775464181711202936"}
22:08:49.809 ->
22:08:49.809 -> 你知道百度嗎?
22:08:49.809 -> Recognition complete
22:08:54.218 -> Start recognition
22:08:54.218 ->
22:08:57.084 -> {"corpus_no":"7349560678205790969","err_msg":"success.","err_no":0,"result":["我喜歡小黃人。"],"sn":"748488478211711202943"}
22:08:57.084 ->
22:08:57.084 -> 我喜歡小黃人。
22:08:57.084 -> Recognition complete
數(shù)據(jù)發(fā)送成功則會返回正確的識別數(shù)據(jù),當然聲音信號不好時返回的語音識別也會不準確。
5. 總結
本文使用Seeed XIAO ESP32S3 Sense開發(fā)板接入百度智能云實現(xiàn)在線語音識別。自帶麥克風模塊用做語音輸入,通過串口發(fā)送字符“1”來控制數(shù)據(jù)的采集和上傳。從而實現(xiàn)對外部世界進行感知,充分認識這個有機與無機的環(huán)境,科學地合理地進行創(chuàng)作和發(fā)揮效益,然后為人類社會發(fā)展貢獻一點微薄之力。??????文章來源:http://www.zghlxwxcb.cn/news/detail-846066.html
- 我會持續(xù)更新對應專欄博客,非常期待你的三連?。?!??????
- 如果鵬鵬有哪里說的不妥,還請大佬多多評論指教!?。??????
- 下面有我的??????群推廣,歡迎志同道合的朋友們加入,期待與你的思維碰撞??????
參考文獻:ESP32接入百度智能云語音識別,實現(xiàn)在線語音識別文章來源地址http://www.zghlxwxcb.cn/news/detail-846066.html
到了這里,關于【ESP32S3 Sense接入百度在線語音識別】的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!