> 技术文档 > 基于ESP32的语音识别与AI大模型对话_esp32离线语音

基于ESP32的语音识别与AI大模型对话_esp32离线语音


一、引言

        本项目基于乐鑫 ESP32-S3 微控制器,结合 ASRPRO 离线语音识别模块,实现了语音控制幻彩 LED 灯光、LCD 显示屏信息交互,以及基于 DeepSeek API 的在线智能问答功能。但是由于对天问语音模块的了解浅薄,不太清楚能否我语音输入什么,它就串口发送什么,所以在AI问答方面有所欠缺,只是验证了能否连接API回答句子,大家汲取有用的即可。(有了解的可以评论区教教博主,真的小白一枚 /(ㄒoㄒ)/~~ )

二、准备工作

 1.相关硬件

乐鑫ESP32-S3 —— 主控芯片,负责语音数据处理与网络通信

ST7735 LCD屏幕 —— 显示语音识别结果、AI回复、状态提示

天问ASRPRO语音模块 —— 离线语音识别,识别用户控制指令(如“开灯”)

麦克风(MIC)—— 采集用户语音

喇叭 —— 播放提示音或语音反馈

WS2812B幻彩LED灯带 —— 可编程 RGB 灯带,用于展示视觉效果

2.模块接线

        我购买的ASRPRO已经装备了mic和喇叭,ESP32也已经接好了ST7723LCD屏幕,大家若购买的单独模块,可以按原理图连接。

ASRPRO原理图

麦克风原理图

喇叭原理图

ESP32-S3原理图

ST7735LCD屏原理图

三、API申请

        我这里选用的网路上免费申请且使用的API,OpenRouter 该网站是一个AI模型聚合网站,先注册登录账号。

        搜索框里搜索要使用的AI模型,我这里用的是deepseek R1(free)。

        点击进入,选择API,点击 Create API key 创建个人API

        填写 Name,点击 Create 即可得到 API key

        最后记录下你申请的 API key 以及使用的模型ID

四、代码实现

1.ASRPRO和ESP32-S3串口通信

        这里两个开发板都使用串口0进行通信,注意 ASRPRO 模块串口0用于编译下载,所以下载语音模块程序时记得先拔掉串口线(拔 5V 和 GND 即可)。

ESP32-S3

ASRPRO

串口通信接线 

 ASRPRO                ESP32-S3

TXD(PB5) ---------> RXD(GPIO44)

RXD(PB6) ---------> TXD(GPIO43)

5V -------------------> 5V      

GND ----------------> GND

 WS2812B 灯带接线

WS2812B                ESP32-S3

Din -------------------> GPIO2 (任意可用的输出引脚均可)

5V --------------------> 5V      

GND -----------------> GND

喇叭接线(插入SPK即可)

2.ASRPRO

3.ESP32-S3

1. 相关库的下载:

  • SPI.h:用于驱动显示屏的 SPI 通信
  • Ucglib.h:用于控制 ST7735 显示屏
  • FastLED.h:控制 WS2812 灯带
  • WiFi.h:用于连接 WiFi
  • HTTPClient.h:发送 HTTP 请求
  • ArduinoJson.h:解析 AI 回复中的 JSON 数据

 2. 软件 SPI 初始化 ST7735 显示屏:

  • SCLK: GPIO18
  • DATA: GPIO17
  • CD (D/C): GPIO16
  • CS: GPIO21
  • RESET: GPIO15

3. 定义 WS2812 灯带参数

  • 30 个灯珠,数据引脚为 GPIO2。

4. WiFi 和 DeepSeek 设置

  • WIFI账号和密码
  • deepseek申请的API key和url

5. 连接 WiFi 函数

  • 开始连接 WiFi
  • 循环等待连接成功
  • WiFi.status() 用来获取 WiFi 当前连接状态
  • WL_CONNECTED 表示已经成功连接上 WiFi
  • 连接成功后提示

6. 调用 DeepSeek AI 接口函数

  • 检查 WiFi 是否已连接

  • 创建 HTTP 客户端对象,用于发送 HTTP 请求(POST)并接收返回。

  • 初始化请求 URL

  • 设置 HTTP 请求头:
  • Content-Type: 指定是发送 JSON 数据。
  • Authorization: 携带 API 密钥认证。
  • HTTP-Referer 和 X-Title:用于平台识别来源和用途。

  • 构造请求体(JSON)

  • 发送 POST 请求并接收响应:
  • http.POST(payload):向服务器发送请求打印服务器原始响应
  • response = http.getString():获取返回的完整 JSON 字符串
  • http.end():关闭连接释放资源

  • 解析 JSON 并提取回复内容

7. setup() 初始化函数

  • 初始化屏幕、串口、LED  
  • 设置字体颜色  
  • 显示提示 “Speak”  
  • 连接 WiFi
  • 初始化 WS2812 LED 控制
  • 设置串口,使用 GPIO43(TX)和 GPIO44(RX)

8. loop() 主循环

  •  如果串口有数据,就读取:  
  • 1. 命令为 on:
  • 2. 命令为 off:
  • 3. 否则 → 发送给 DeepSeek
  • 最后屏幕分行显示 AI 回复

五、完整源码 

1.ASRPRO

#include \"asr.h\"extern \"C\"{ void * __dso_handle = 0 ;}#include \"setup.h\"#include \"myLib/asr_event.h\"uint32_t snid;//{speak:小爱-活泼女声,vol:2,speed:10,platform:haohaodada,version:V3}//{playid:10001,voice:欢迎使用语音助手,用小爱小爱唤醒我。}//{playid:10002,voice:我退下了,用小爱小爱唤醒我}void ASR_CODE(){ //{ID:10500,keyword:\"唤醒词\",ASR:\"小爱小爱\",ASRTO:\"主人,我在呢\"} if(snid == 10500){ } //{ID:10501,keyword:\"命令词\",ASR:\"开灯\",ASRTO:\"已经开灯了呦\"} if(snid == 10501){ Serial.println(\"on\"); } //{ID:10502,keyword:\"命令词\",ASR:\"关灯\",ASRTO:\"已经关灯了呦\"} if(snid == 10502){ Serial.println(\"off\"); } //{ID:10503,keyword:\"命令词\",ASR:\"你好\",ASRTO:\"\"} if(snid == 10503){ Serial.println(\"hello\"); } //{ID:10504,keyword:\"命令词\",ASR:\"你叫什么\",ASRTO:\"\"} if(snid == 10504){ Serial.println(\"what is your name\"); } set_state_enter_wakeup(15000);}void hardware_init(){ vol_set(2); vTaskDelete(NULL);}void setup(){ set_gpio_input(4); set_gpio_input(29); setPinFun(13,SECOND_FUNCTION); setPinFun(14,SECOND_FUNCTION); Serial.begin(115200);}

2.ESP32-S3

记得改 wifi 账号密码和 api key

#include #include \"Ucglib.h\"#include #include #include #include  Ucglib_ST7735_18x128x160_SWSPI ucg(/*sclk=*/ 18, /*data=*/ 17, /*cd=*/ 16, /*cs=*/ 21, /*reset=*/ 15);#define RXD0 44#define TXD0 43// #define LED_PIN 2 #define NUM_LEDS 30 // 灯珠总数为30个#define DATA_PIN 2 // WS2812 DIN引脚接2#define LED_TYPE WS2812#define COLOR_ORDER GRBuint8_t max_bright = 128;CRGB leds[NUM_LEDS];const char* ssid = \"XXX\";const char* password = \"XXXXXX\";const char* deepseek_api_key = \"XXXXXX\"; const char* deepseek_api_url = \"https://openrouter.ai/api/v1\";void connectWiFi() { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.println(\"连接WiFi中...\"); } Serial.println(\"WiFi已连接\");}String askDeepSeek(String question) { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; http.begin(\"https://openrouter.ai/api/v1/chat/completions\"); http.addHeader(\"Content-Type\", \"application/json\"); http.addHeader(\"Authorization\", \"Bearer \" + String(deepseek_api_key)); http.addHeader(\"HTTP-Referer\", \"https://yourproject.example.com\"); http.addHeader(\"X-Title\", \"ESP32_DeepSeek\"); String payload = \"{\"; payload += \"\\\"model\\\": \\\"deepseek/deepseek-r1:free\\\",\"; payload += \"\\\"messages\\\": [{\\\"role\\\": \\\"user\\\", \\\"content\\\": \\\"\" + question + \"\\\"}]\"; payload += \"}\"; int httpResponseCode = http.POST(payload); String response = http.getString(); http.end(); Serial.println(\"Response:\"); Serial.println(response); if (httpResponseCode == 200) { DynamicJsonDocument doc(8192); DeserializationError error = deserializeJson(doc, response); if (!error) { return doc[\"choices\"][0][\"message\"][\"content\"].as(); } else { return \"解析JSON失败\"; } } else { return \"错误码:\" + String(httpResponseCode) + \"\\n\" + response; } } else { return \"WiFi未连接\"; }}void setup(void){ delay(1000); ucg.begin(UCG_FONT_MODE_TRANSPARENT); ucg.clearScreen(); ucg.setFont(ucg_font_ncenR12_tr); ucg.setColor(255, 255, 255); // 初始化串口和 LED // pinMode(LED_PIN, OUTPUT); FastLED.addLeds(leds, NUM_LEDS); FastLED.setBrightness(max_bright); Serial.begin(115200, SERIAL_8N1, RXD0, TXD0); // 显示初始信息 ucg.setPrintPos(0, 25); ucg.print(\" Speak \"); connectWiFi();}void loop(void){ if (Serial.available()) { String cmd = Serial.readStringUntil(\'\\n\'); cmd.trim(); if (cmd == \"on\") { // 点亮所有 WS2812 灯为红色 for (int i = 0; i < NUM_LEDS; i++) { leds[i] = CRGB::Red; } FastLED.show(); ucg.clearScreen(); ucg.setPrintPos(0, 25); ucg.print(\"LED ON\"); Serial.println(\"LED ON\"); } else if (cmd == \"off\") { // 关闭所有灯 for (int i = 0; i < NUM_LEDS; i++) { leds[i] = CRGB::Black; } FastLED.show(); ucg.clearScreen(); ucg.setPrintPos(0, 25); ucg.print(\"LED OFF\"); Serial.println(\"LED OFF\"); } else { // 非开关命令 → 发给DeepSeek处理 ucg.clearScreen(); ucg.setPrintPos(0, 25); ucg.print(\"thinking...\"); String response = askDeepSeek(cmd); Serial.println(\"DeepSeek : \" + response); // 显示回复 ucg.clearScreen(); // 清屏 ucg.setColor(255, 255, 255); // 设置白色字体 ucg.setPrintPos(0, 20); // 设置初始显示位置 // 为避免内容过长,建议分页或裁剪显示 int maxCharsPerLine = 15; // 根据屏幕宽度调整 int lineHeight = 12; // 行高,根据字体调整 int y = 20; // 初始y坐标 for (int i = 0; i < response.length(); i += maxCharsPerLine) { String line = response.substring(i, i + maxCharsPerLine); ucg.setPrintPos(0, y); ucg.print(line); y += lineHeight; } } } }