ESP32 + MicroPython 实现音频录制与播放的完整方案

张开发
2026/4/9 10:39:52 15 分钟阅读

分享文章

ESP32 + MicroPython 实现音频录制与播放的完整方案
1. 硬件准备与连接指南想要用ESP32实现音频录制与播放首先需要准备好核心硬件。我推荐使用ESP32开发板搭配INMP441麦克风模块和MAX98357音频放大器模块的组合这个方案成本低、效果好实测在智能家居和语音交互项目中都非常稳定。必备硬件清单ESP32开发板建议选择带有SPI闪存的版本INMP441数字麦克风模块注意要买3.3V供电版本MAX98357 I2S音频放大器模块3W小喇叭阻抗4-8欧姆杜邦线若干建议用彩色线区分功能我第一次搭建时犯了个低级错误把INMP441的左右声道选择引脚悬空了结果录音全是杂音。后来发现必须把L/R引脚明确接地才能选择左声道输入。正确的接线方式应该是INMP441 → ESP32 3.3V → 3.3V GND → GND SD → GPIO21 WS → GPIO22 SCK → GPIO23 L/R → GND MAX98357 → ESP32 VIN → 5V GND → GND DIN → GPIO13 BCLK → GPIO12 LRC → GPIO14注意虽然MAX98357支持3.3V-5V供电但实测5V供电时喇叭输出功率更大。如果使用3.3V供电音量会明显变小。2. MicroPython环境配置很多新手卡在第一步的固件刷写上我分享一个实测可用的方案。首先需要下载支持I2S功能的MicroPython固件推荐使用v1.20.0之后的版本这个版本对音频处理做了专门优化。刷写步骤安装esptoolpip install esptool擦除原有固件esptool.py --port COM3 erase_flash写入新固件esptool.py --port COM3 --baud 460800 write_flash -z 0x1000 firmware.bin刷机完成后建议立即测试I2S功能是否正常。这里有个快速检测脚本from machine import I2S, Pin i2s I2S(0, sckPin(23), wsPin(22), sdPin(21), modeI2S.RX, bits16, formatI2S.MONO, rate16000, ibuf40000) print(I2S初始化成功)如果看到内存分配错误可能是固件版本不对。我遇到过v1.19版本无法分配大缓冲区的坑升级到v1.20就解决了。3. 音频录制实战录音功能的核心是正确处理WAV文件头。原始代码中的createWavHeader函数虽然能用但缺乏灵活性。我改进后的版本支持动态调整采样率和声道数def create_wav_header(sample_rate16000, bits_per_sample16, num_channels1, duration_sec10): byte_rate sample_rate * num_channels * bits_per_sample // 8 block_align num_channels * bits_per_sample // 8 datasize byte_rate * duration_sec header bytes(RIFF, ascii) header (datasize 36).to_bytes(4, little) header bytes(WAVE, ascii) header bytes(fmt , ascii) header (16).to_bytes(4, little) # fmt chunk size header (1).to_bytes(2, little) # PCM format header num_channels.to_bytes(2, little) header sample_rate.to_bytes(4, little) header byte_rate.to_bytes(4, little) header block_align.to_bytes(2, little) header bits_per_sample.to_bytes(2, little) header bytes(data, ascii) header datasize.to_bytes(4, little) return header录音时常见的问题有三个采样率设置过高导致内存不足 - ESP32在16000Hz采样率下最多支持10秒立体声缓冲区大小不合适会产生爆音 - 建议设置为32768字节未及时关闭文件会导致写入不完整我建议添加实时音量监测功能方便调试def calculate_rms(audio_data): sum_squares 0 for i in range(0, len(audio_data), 2): sample int.from_bytes(audio_data[i:i2], little, signedTrue) sum_squares sample * sample return (sum_squares / (len(audio_data)//2)) ** 0.54. 音频播放优化技巧原始播放代码直接读取整个文件这在长音频时会导致内存溢出。我改进为流式播放方案def stream_playback(filename, chunk_size1024): audio_out I2S(1, sckPin(12), wsPin(14), sdPin(13), modeI2S.TX, bits16, formatI2S.STEREO, rate16000, ibuf20000) with open(filename, rb) as f: f.seek(44) # Skip WAV header while True: chunk f.read(chunk_size) if not chunk: break while len(chunk) % 4 ! 0: # Ensure alignment chunk b\x00 audio_out.write(chunk) audio_out.deinit()针对MAX98357模块的几个优化点添加软音量控制def set_volume(level): # level: 0-100 attenuation int((100 - level) * 2.55) i2c I2C(0, sclPin(25), sdaPin(26)) i2c.writeto(0x20, bytes([0xFE, attenuation]))增加音频淡入淡出效果支持播放中断和恢复5. 常见问题排查问题1录音全是噪声检查INMP441的L/R引脚是否接地确认采样率与硬件匹配INMP441最高支持48kHz尝试在代码中添加去直流偏移audio_data bytes([x ^ 0x80 for x in audio_data])问题2播放时有爆音检查电源是否稳定建议并联100μF电容调整I2S缓冲区大小20000-40000之间确保WAV文件头信息正确问题3内存不足降低采样率8000Hz够语音使用改用单声道录制缩短录制时长我在项目中最常遇到的是电源干扰问题后来发现给MAX98357单独供电就解决了。如果使用电池供电建议在3.3V和GND之间加一个100μF的电解电容。6. 进阶应用示例结合上述功能我们可以实现更实用的语音备忘录应用。这个例子包含定时录音、自动播放和文件管理import uos class VoiceMemo: def __init__(self): self.files [] self.update_file_list() def update_file_list(self): self.files [f for f in uos.listdir() if f.endswith(.wav)] def record_new(self, duration10): filename fmemo_{len(self.files)1}.wav start_in(filename, durationduration) self.update_file_list() def play_all(self): for f in self.files: print(fPlaying {f}...) stream_playback(f)还可以添加网络功能通过HTTP接口控制录音import network import usocket as socket ap network.WLAN(network.AP_IF) ap.config(essidESP32-Recorder, password12345678) ap.active(True) s socket.socket() s.bind((, 80)) s.listen(5) while True: conn, addr s.accept() request conn.recv(1024) if b/record in request: VoiceMemo().record_new() conn.send(HTTP/1.1 200 OK\nRecording started) conn.close()实际开发中发现同时运行WiFi和I2S会导致音频质量下降。解决方法是在录音时暂时关闭WiFi或者使用双核分别处理网络和音频任务。

更多文章