34浏览
查看: 34|回复: 0

[Micropython] 玩转DFRobot ESP32-P4开发板,用Micropython仿制小智

[复制链接]
本帖最后由 PY学习笔记 于 2025-8-25 17:07 编辑

近期,DFRobot 推出了全新开发板 FireBeetle 2 ESP32-P4。这块开发板搭载了 ESP32-P4 主控,虽未集成 Wi-Fi 与蓝牙,但凭借强劲性能,依然令人眼前一亮。很荣幸能抢先体验这块开发板!
1.开发板介绍
FireBeetle 2 ESP32-P4有很多种外设:
  • Type-C USB CDC:Type-C USB烧录、调试接口
  • IO3/LED:板载LED引脚
  • Power LED:主板电源指示灯
  • RST:复位按键
  • IO35/BOOT:IO引脚/BOOT按键
  • MIC: MEMS PDM麦克风
  • HIGH-SPEED USB OTG 2.0: Type-C高速USB OTG 2.0
  • ESP32-P4:ESP32-P4芯片
  • MIPI-DSI: 两通道MIPI-DSI屏幕(兼容树莓派4B DSI屏幕线序)
  • MIPI-CSI: 两通道MIPI-DSI屏幕(兼容树莓派4B CSI摄像头线序)
  • TF Card: TF卡插槽
  • 16MB FLASH: 16MB Flash存储
  • ESP32-C6:ESP32-C6-MINI-1模组,通过SDIO与ESP32-P4连接,用于扩展WiFi、蓝牙

2.实现原理
由于MicroPython不支持离线语音识别与合成,所以采用在线语音识别和在线语音合成,AI也是采用在线的方式,在线语音识别和AI大模型均使用siliconflow的免费模型,而在线语音合成使用了百度的语音合成(实名认证后免费5万次),再结合ST7789屏幕(使用MIPI屏幕会出问题),MAX9835功放模块,以及板载的PDM麦克风即可实现。
3.代码实现
参考代码如下:
  1. # 录音+语音识别+AI对话+语音合成的完整闭环
  2. import network, urequests, ujson, gc, time, st7789_spi, baidu_tts
  3. from easydisplay import EasyDisplay
  4. from machine import Pin, I2S, SPI
  5. # ---------- 全局配置 ----------
  6. WIFI_SSID = "SSID"
  7. WIFI_PASS = "PWD"
  8. API_KEY   = "API-KEY"
  9. TTS_API_KEY = "API-KEY"
  10. TTS_SEC_KEY = "SEC-KEY"
  11. ASR_URL   = "https://api.siliconflow.cn/v1/audio/transcriptions"
  12. CHAT_URL  = "https://api.siliconflow.cn/v1/chat/completions"
  13. ASR_MODEL = "FunAudioLLM/SenseVoiceSmall"
  14. LLM_MODEL = "Qwen/Qwen3-8B"
  15. # I2S 引脚
  16. SCK_PIN = 12
  17. SD_PIN  = 9
  18. boot = Pin(35, Pin.IN, Pin.PULL_UP)   # BOOT 键,按下为 0
  19. led = Pin(3,Pin.OUT)
  20. spi = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=Pin(28), mosi=Pin(29))
  21. dp = st7789_spi.ST7789(width=240, height=280, spi=spi, cs=20, dc=4, res=30, rotate=1,invert=False, rgb=False)
  22. ed = EasyDisplay(dp, "RGB565", font="/text_lite_16px_2312.v3.bmf", show=True, color=0xFFFF, clear=True,auto_wrap=True)
  23. # ---------- 联网 ----------
  24. def connect_wifi():
  25.     sta = network.WLAN(network.STA_IF)
  26.     sta.active(True)
  27.     sta.connect(WIFI_SSID, WIFI_PASS)
  28.     while not sta.isconnected():
  29.         time.sleep(0.5)
  30.     print("Wi-Fi OK:", sta.ifconfig()[0])
  31.     return sta
  32. def record_audio(sr=8000):
  33.     # 等待按下
  34.     print("长按 BOOT 开始录音...")
  35.     while boot.value() == 1:
  36.         time.sleep_ms(10)
  37.     # 开始录音
  38.     pcm = bytearray()
  39.     audio = I2S(0,
  40.                 sck=Pin(SCK_PIN),
  41.                 sd=Pin(SD_PIN),
  42.                 mode=I2S.PDM_RX,
  43.                 bits=16,
  44.                 format=I2S.MONO,
  45.                 rate=sr * 4,
  46.                 ibuf=10240)
  47.     # 边录边检查按键
  48.     chunk = bytearray(1024)
  49.     print("录音中,松开 BOOT 结束...")
  50.     while boot.value() == 0:           # 0 表示仍按着
  51.         n = audio.readinto(chunk)
  52.         pcm.extend(chunk[:n])
  53.     audio.deinit()
  54.     return pcm, sr
  55. # ---------- 构造 WAV ----------
  56. def wav_header(data_len, sample_rate):
  57.     hdr = bytearray(44)
  58.     hdr[0:4]   = b'RIFF'
  59.     hdr[4:8]   = (data_len + 36).to_bytes(4, 'little')
  60.     hdr[8:12]  = b'WAVE'
  61.     hdr[12:16] = b'fmt '
  62.     hdr[16:20] = (16).to_bytes(4, 'little')
  63.     hdr[20:22] = (1).to_bytes(2, 'little')         # PCM
  64.     hdr[22:24] = (1).to_bytes(2, 'little')         # mono
  65.     hdr[24:28] = sample_rate.to_bytes(4, 'little')
  66.     hdr[28:32] = (sample_rate * 2).to_bytes(4, 'little')
  67.     hdr[32:34] = (2).to_bytes(2, 'little')         # block align
  68.     hdr[34:36] = (16).to_bytes(2, 'little')        # bits per sample
  69.     hdr[36:40] = b'data'
  70.     hdr[40:44] = data_len.to_bytes(4, 'little')
  71.     return hdr
  72. # ---------- 语音识别 ----------
  73. def speech_to_text(pcm, sr):
  74.     wav = wav_header(len(pcm), sr) + pcm
  75.     boundary = "----VoiceBoundary"
  76.     body  = b"--" + boundary.encode() + b"\r\n"
  77.     body += b'Content-Disposition: form-data; name="file"; filename="mic.wav"\r\n'
  78.     body += b"Content-Type: audio/wav\r\n\r\n"
  79.     body += wav
  80.     body += b"\r\n--" + boundary.encode() + b"\r\n"
  81.     body += b'Content-Disposition: form-data; name="model"\r\n\r\n'
  82.     body += ASR_MODEL.encode()
  83.     body += b"\r\n--" + boundary.encode() + b"--\r\n"
  84.     headers = {
  85.         "Authorization": "Bearer " + API_KEY,
  86.         "Content-Type": "multipart/form-data; boundary=" + boundary
  87.     }
  88.     print("识别中…")
  89.     res = urequests.post(ASR_URL, data=body, headers=headers)
  90.     text = res.json().get("text", "").strip()
  91.     res.close()
  92.     gc.collect()
  93.     return text
  94. # ---------- 对话 ----------
  95. def chat_with_ai(text):
  96.     headers = {
  97.         "Authorization": "Bearer " + API_KEY,
  98.         "Content-Type": "application/json"
  99.     }
  100.     payload = {
  101.         "model": LLM_MODEL,
  102.         "messages": [
  103.             {"role": "system", "content": "你是我的AI助手小智,你必须用中文回答且不超过100字还不允许使用MD进行回答"},
  104.             {"role": "user", "content": text}
  105.         ],
  106.         "enable_thinking":False,
  107.     }
  108.     print("AI思考中…")
  109.     start = time.time()
  110.     res = urequests.post(CHAT_URL, data=ujson.dumps(payload).encode(), headers=headers)
  111.     delta = time.time() - start
  112.     if res.status_code == 200:
  113.         reply = res.json()['choices'][0]['message']['content'].replace("\n", "")
  114.         print(f"({delta:.1f}s) AI:", reply)
  115.         ed.text(f"({delta:.1f}s) AI:"+reply, 0, 50)
  116.         baidu_tts.run(
  117.             access=TTS_API_KEY,
  118.             secret=TTS_SEC_KEY,
  119.             text=reply,
  120.         )
  121.     else:
  122.         print("Error:", res.status_code, res.text)
  123.         reply = ""
  124.     res.close()
  125.     gc.collect()
  126.     return reply
  127. # ---------- 主循环 ----------
  128. def main():
  129.     connect_wifi()
  130.     while True:
  131.         pcm, sr = record_audio()
  132.         text = speech_to_text(pcm, sr)
  133.         if not text:
  134.             print("没听清,请再说一遍")
  135.             baidu_tts.run(
  136.                 access=TTS_API_KEY,
  137.                 secret=TTS_SEC_KEY,
  138.                 text="没听清,请再说一遍",
  139.                 out_path='welcome.wav'
  140.             )
  141.             continue
  142.         elif "开灯" in text:
  143.             print("你:", text)
  144.             led.on()
  145.             print("AI:LED灯已开启")
  146.             ed.text("AI:LED灯已开启", 0, 50)
  147.             baidu_tts.run(
  148.                 access=TTS_API_KEY,
  149.                 secret=TTS_SEC_KEY,
  150.                 text="LED灯已开启",
  151.             )
  152.         elif "关灯" in text:
  153.             print("你:", text)
  154.             led.off()
  155.             print("AI:LED灯已关闭")
  156.             ed.text("AI:LED灯已关闭", 0, 50)
  157.             baidu_tts.run(
  158.                 access=TTS_API_KEY,
  159.                 secret=TTS_SEC_KEY,
  160.                 text="LED灯已关闭",
  161.             )
  162.         else:
  163.             print("你:", text)
  164.             chat_with_ai(text)
  165. if __name__ == "__main__":
  166.     main()
复制代码


4.效果



您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail