PY学习笔记 发表于 2025-8-25 16:29:50

玩转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.代码实现参考代码如下:# 录音+语音识别+AI对话+语音合成的完整闭环
import network, urequests, ujson, gc, time, st7789_spi, baidu_tts
from easydisplay import EasyDisplay
from machine import Pin, I2S, SPI
# ---------- 全局配置 ----------
WIFI_SSID = "SSID"
WIFI_PASS = "PWD"
API_KEY   = "API-KEY"
TTS_API_KEY = "API-KEY"
TTS_SEC_KEY = "SEC-KEY"

ASR_URL   = "https://api.siliconflow.cn/v1/audio/transcriptions"
CHAT_URL= "https://api.siliconflow.cn/v1/chat/completions"

ASR_MODEL = "FunAudioLLM/SenseVoiceSmall"
LLM_MODEL = "Qwen/Qwen3-8B"

# I2S 引脚
SCK_PIN = 12
SD_PIN= 9
boot = Pin(35, Pin.IN, Pin.PULL_UP)   # BOOT 键,按下为 0
led = Pin(3,Pin.OUT)
spi = SPI(2, baudrate=20000000, polarity=0, phase=0, sck=Pin(28), mosi=Pin(29))
dp = st7789_spi.ST7789(width=240, height=280, spi=spi, cs=20, dc=4, res=30, rotate=1,invert=False, rgb=False)
ed = EasyDisplay(dp, "RGB565", font="/text_lite_16px_2312.v3.bmf", show=True, color=0xFFFF, clear=True,auto_wrap=True)
# ---------- 联网 ----------
def connect_wifi():
    sta = network.WLAN(network.STA_IF)
    sta.active(True)
    sta.connect(WIFI_SSID, WIFI_PASS)
    while not sta.isconnected():
      time.sleep(0.5)
    print("Wi-Fi OK:", sta.ifconfig())
    return sta

def record_audio(sr=8000):
    # 等待按下
    print("长按 BOOT 开始录音...")
    while boot.value() == 1:
      time.sleep_ms(10)

    # 开始录音
    pcm = bytearray()
    audio = I2S(0,
                sck=Pin(SCK_PIN),
                sd=Pin(SD_PIN),
                mode=I2S.PDM_RX,
                bits=16,
                format=I2S.MONO,
                rate=sr * 4,
                ibuf=10240)

    # 边录边检查按键
    chunk = bytearray(1024)
    print("录音中,松开 BOOT 结束...")
    while boot.value() == 0:         # 0 表示仍按着
      n = audio.readinto(chunk)
      pcm.extend(chunk[:n])

    audio.deinit()
    return pcm, sr

# ---------- 构造 WAV ----------
def wav_header(data_len, sample_rate):
    hdr = bytearray(44)
    hdr   = b'RIFF'
    hdr   = (data_len + 36).to_bytes(4, 'little')
    hdr= b'WAVE'
    hdr = b'fmt '
    hdr = (16).to_bytes(4, 'little')
    hdr = (1).to_bytes(2, 'little')         # PCM
    hdr = (1).to_bytes(2, 'little')         # mono
    hdr = sample_rate.to_bytes(4, 'little')
    hdr = (sample_rate * 2).to_bytes(4, 'little')
    hdr = (2).to_bytes(2, 'little')         # block align
    hdr = (16).to_bytes(2, 'little')      # bits per sample
    hdr = b'data'
    hdr = data_len.to_bytes(4, 'little')
    return hdr

# ---------- 语音识别 ----------
def speech_to_text(pcm, sr):
    wav = wav_header(len(pcm), sr) + pcm
    boundary = "----VoiceBoundary"
    body= b"--" + boundary.encode() + b"\r\n"
    body += b'Content-Disposition: form-data; name="file"; filename="mic.wav"\r\n'
    body += b"Content-Type: audio/wav\r\n\r\n"
    body += wav
    body += b"\r\n--" + boundary.encode() + b"\r\n"
    body += b'Content-Disposition: form-data; name="model"\r\n\r\n'
    body += ASR_MODEL.encode()
    body += b"\r\n--" + boundary.encode() + b"--\r\n"

    headers = {
      "Authorization": "Bearer " + API_KEY,
      "Content-Type": "multipart/form-data; boundary=" + boundary
    }
    print("识别中…")
    res = urequests.post(ASR_URL, data=body, headers=headers)
    text = res.json().get("text", "").strip()
    res.close()
    gc.collect()
    return text

# ---------- 对话 ----------
def chat_with_ai(text):
    headers = {
      "Authorization": "Bearer " + API_KEY,
      "Content-Type": "application/json"
    }
    payload = {
      "model": LLM_MODEL,
      "messages": [
            {"role": "system", "content": "你是我的AI助手小智,你必须用中文回答且不超过100字还不允许使用MD进行回答"},
            {"role": "user", "content": text}
      ],
      "enable_thinking":False,
    }
    print("AI思考中…")
    start = time.time()
    res = urequests.post(CHAT_URL, data=ujson.dumps(payload).encode(), headers=headers)
    delta = time.time() - start
    if res.status_code == 200:
      reply = res.json()['choices']['message']['content'].replace("\n", "")
      print(f"({delta:.1f}s) AI:", reply)
      ed.text(f"({delta:.1f}s) AI:"+reply, 0, 50)
      baidu_tts.run(
            access=TTS_API_KEY,
            secret=TTS_SEC_KEY,
            text=reply,
      )
    else:
      print("Error:", res.status_code, res.text)
      reply = ""
    res.close()
    gc.collect()
    return reply

# ---------- 主循环 ----------
def main():
    connect_wifi()
    while True:
      pcm, sr = record_audio()
      text = speech_to_text(pcm, sr)
      if not text:
            print("没听清,请再说一遍")
            baidu_tts.run(
                access=TTS_API_KEY,
                secret=TTS_SEC_KEY,
                text="没听清,请再说一遍",
                out_path='welcome.wav'
            )
            continue
      elif "开灯" in text:
            print("你:", text)
            led.on()
            print("AI:LED灯已开启")
            ed.text("AI:LED灯已开启", 0, 50)
            baidu_tts.run(
                access=TTS_API_KEY,
                secret=TTS_SEC_KEY,
                text="LED灯已开启",
            )
      elif "关灯" in text:
            print("你:", text)
            led.off()
            print("AI:LED灯已关闭")
            ed.text("AI:LED灯已关闭", 0, 50)
            baidu_tts.run(
                access=TTS_API_KEY,
                secret=TTS_SEC_KEY,
                text="LED灯已关闭",
            )
      else:
            print("你:", text)
            chat_with_ai(text)

if __name__ == "__main__":
    main()

4.效果
https://www.bilibili.com/video/BV1SyeRz6E5D/


页: [1]
查看完整版本: 玩转DFRobot ESP32-P4开发板,用Micropython仿制小智