ESP32 / STM32 HTTP Audio Streaming Test Platform
本平台专为ESP32、STM32等微控制器提供在线音频文件访问服务,用于测试和开发基于HTTP协议的网络音频播放功能。
// 11025Hz 采样率 http://audio.wuhanqing.cn/sound/Sparkle_mono_11025.wav // 22050Hz 采样率 http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav // 44100Hz 采样率(高质量) http://audio.wuhanqing.cn/sound/Sparkle_mono_44100.wav
import urequests
import network
from machine import I2S, Pin
import time
# WiFi配置
SSID = "你的WiFi名称"
PASSWORD = "你的WiFi密码"
# 音频文件URL
AUDIO_URL = "http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav"
# I2S配置
SCK_PIN = 25 # BCK (Bit Clock)
WS_PIN = 26 # WS (Word Select / LRCK)
SD_PIN = 22 # SD (Serial Data)
SAMPLE_RATE = 22050 # 采样率需要与音频文件匹配
BITS_PER_SAMPLE = 16
def connect_wifi():
"""连接WiFi"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('正在连接WiFi...')
wlan.connect(SSID, PASSWORD)
timeout = 0
while not wlan.isconnected() and timeout < 20:
time.sleep(1)
timeout += 1
print('.', end='')
if wlan.isconnected():
print('\nWiFi连接成功!')
print('IP地址:', wlan.ifconfig()[0])
return True
else:
print('\nWiFi连接失败!')
return False
return True
def parse_wav_header(data):
"""解析WAV文件头"""
if data[0:4] != b'RIFF':
raise ValueError("不是有效的WAV文件")
# 查找data chunk
pos = 12
while pos < len(data):
chunk_id = data[pos:pos+4]
chunk_size = int.from_bytes(data[pos+4:pos+8], 'little')
if chunk_id == b'data':
return pos + 8 # 返回音频数据开始位置
pos += 8 + chunk_size
raise ValueError("未找到音频数据")
def play_audio_stream():
"""播放在线音频"""
try:
# 配置I2S
audio_out = I2S(
0,
sck=Pin(SCK_PIN),
ws=Pin(WS_PIN),
sd=Pin(SD_PIN),
mode=I2S.TX,
bits=BITS_PER_SAMPLE,
format=I2S.MONO,
rate=SAMPLE_RATE,
ibuf=20000
)
print('开始下载音频文件...')
print('URL:', AUDIO_URL)
# 发送HTTP GET请求
response = urequests.get(AUDIO_URL, stream=True)
if response.status_code != 200:
print(f'HTTP错误: {response.status_code}')
return
print('开始播放音频...')
# 读取并跳过WAV文件头
header_data = response.raw.read(200)
data_start = parse_wav_header(header_data)
# 如果data_start在已读取的数据范围内,调整位置
if data_start < 200:
audio_data = header_data[data_start:]
audio_out.write(audio_data)
# 流式播放音频数据
chunk_size = 4096
total_bytes = 0
while True:
audio_data = response.raw.read(chunk_size)
if not audio_data:
break
audio_out.write(audio_data)
total_bytes += len(audio_data)
if total_bytes % (chunk_size * 10) == 0:
print(f'已播放: {total_bytes} 字节')
print(f'播放完成! 总计: {total_bytes} 字节')
# 清理资源
audio_out.deinit()
response.close()
except Exception as e:
print('播放错误:', e)
import sys
sys.print_exception(e)
# 主程序
if __name__ == '__main__':
print('=== ESP32 网络音频播放器 ===')
if connect_wifi():
time.sleep(2)
play_audio_stream()
else:
print('无法连接WiFi,程序退出')
#include <WiFi.h>
#include <HTTPClient.h>
#include <VS1053.h> // 需要安装 VS1053 库
// WiFi配置
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
// 音频URL
const char* audioURL = "http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav";
// VS1053引脚定义
#define VS1053_CS 5 // XCS
#define VS1053_DCS 4 // XDCS
#define VS1053_DREQ 2 // DREQ
VS1053 player(VS1053_CS, VS1053_DCS, VS1053_DREQ);
void setup() {
Serial.begin(115200);
Serial.println("\n=== ESP32 网络音频播放器 ===");
// 初始化VS1053
SPI.begin();
player.begin();
player.setVolume(80); // 音量 0-100
// 连接WiFi
connectWiFi();
}
void loop() {
if (WiFi.status() == WL_CONNECTED) {
playAudioFromURL(audioURL);
delay(5000); // 播放完成后等待5秒
} else {
Serial.println("WiFi未连接,正在重连...");
connectWiFi();
}
}
void connectWiFi() {
Serial.print("正在连接WiFi: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
int timeout = 0;
while (WiFi.status() != WL_CONNECTED && timeout < 20) {
delay(500);
Serial.print(".");
timeout++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi连接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("\nWiFi连接失败!");
}
}
void playAudioFromURL(const char* url) {
HTTPClient http;
Serial.println("开始下载音频...");
Serial.print("URL: ");
Serial.println(url);
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
WiFiClient* stream = http.getStreamPtr();
int totalBytes = http.getSize();
int bytesRead = 0;
Serial.println("开始播放音频...");
Serial.print("文件大小: ");
Serial.print(totalBytes);
Serial.println(" 字节");
// 跳过WAV文件头(44字节)
uint8_t header[44];
stream->readBytes(header, 44);
bytesRead += 44;
// 流式播放音频数据
const int bufferSize = 32;
uint8_t buffer[bufferSize];
while (http.connected() && (bytesRead < totalBytes || totalBytes == -1)) {
size_t available = stream->available();
if (available) {
int c = stream->readBytes(buffer, min((size_t)bufferSize, available));
// 发送到VS1053
player.playChunk(buffer, c);
bytesRead += c;
if (bytesRead % 10240 == 0) {
Serial.print("已播放: ");
Serial.print(bytesRead);
Serial.print(" / ");
Serial.print(totalBytes);
Serial.println(" 字节");
}
}
delay(1);
}
Serial.println("播放完成!");
} else {
Serial.print("HTTP错误: ");
Serial.println(httpCode);
}
http.end();
}
/* main.c */
#include "main.h"
#include "string.h"
#include "stdio.h"
// UART和TIM句柄
UART_HandleTypeDef huart1; // ESP8266通信
UART_HandleTypeDef huart2; // 调试串口
TIM_HandleTypeDef htim1; // PWM for DAC
// 配置
#define WIFI_SSID "你的WiFi名称"
#define WIFI_PASSWORD "你的WiFi密码"
#define AUDIO_URL "audio.wuhanqing.cn"
#define AUDIO_PATH "/sound/Sparkle_mono_11025.wav"
// 缓冲区
#define RX_BUFFER_SIZE 1024
uint8_t rxBuffer[RX_BUFFER_SIZE];
uint16_t rxIndex = 0;
// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM1_Init(void);
void ESP8266_Init(void);
void ESP8266_SendCommand(char* cmd, char* expected, uint32_t timeout);
void PlayAudioFromHTTP(void);
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
MX_TIM1_Init();
// 启动PWM
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
printf("=== STM32 网络音频播放器 ===\r\n");
// 初始化ESP8266
ESP8266_Init();
// 播放音频
while(1) {
PlayAudioFromHTTP();
HAL_Delay(5000);
}
}
void ESP8266_Init(void) {
printf("初始化ESP8266...\r\n");
// 复位模块
ESP8266_SendCommand("AT+RST\r\n", "OK", 2000);
HAL_Delay(3000);
// 设置为Station模式
ESP8266_SendCommand("AT+CWMODE=1\r\n", "OK", 1000);
// 连接WiFi
char cmd[100];
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_SSID, WIFI_PASSWORD);
ESP8266_SendCommand(cmd, "OK", 10000);
printf("WiFi连接成功!\r\n");
}
void ESP8266_SendCommand(char* cmd, char* expected, uint32_t timeout) {
memset(rxBuffer, 0, RX_BUFFER_SIZE);
rxIndex = 0;
HAL_UART_Transmit(&huart1, (uint8_t*)cmd, strlen(cmd), 1000);
uint32_t startTime = HAL_GetTick();
while (HAL_GetTick() - startTime < timeout) {
if (HAL_UART_Receive(&huart1, &rxBuffer[rxIndex], 1, 10) == HAL_OK) {
rxIndex++;
if (strstr((char*)rxBuffer, expected) != NULL) {
return;
}
}
}
}
void PlayAudioFromHTTP(void) {
char cmd[200];
printf("开始下载音频...\r\n");
// 建立TCP连接
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",80\r\n", AUDIO_URL);
ESP8266_SendCommand(cmd, "OK", 5000);
// 构建HTTP GET请求
char httpRequest[300];
sprintf(httpRequest,
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"Connection: close\r\n\r\n",
AUDIO_PATH, AUDIO_URL);
// 发送HTTP请求
sprintf(cmd, "AT+CIPSEND=%d\r\n", strlen(httpRequest));
ESP8266_SendCommand(cmd, ">", 1000);
HAL_UART_Transmit(&huart1, (uint8_t*)httpRequest, strlen(httpRequest), 2000);
printf("开始播放音频...\r\n");
// 接收并播放音频数据
uint8_t audioByte;
uint32_t bytesReceived = 0;
int headerSkipped = 0;
int headerBytes = 0;
while (1) {
if (HAL_UART_Receive(&huart1, &audioByte, 1, 100) == HAL_OK) {
// 跳过HTTP头和WAV头
if (!headerSkipped) {
headerBytes++;
if (headerBytes > 500) { // 简单处理:跳过前500字节
headerSkipped = 1;
printf("开始输出音频\r\n");
}
continue;
}
// 使用PWM输出音频(8位DAC)
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, audioByte);
bytesReceived++;
if (bytesReceived % 1000 == 0) {
printf("已播放: %lu 字节\r\n", bytesReceived);
}
// 简单的采样率控制(11025Hz约为90us间隔)
HAL_Delay(0); // 可根据实际调整
} else {
// 接收超时,认为传输完成
if (headerSkipped && bytesReceived > 1000) {
break;
}
}
}
printf("播放完成! 总计: %lu 字节\r\n", bytesReceived);
// 关闭连接
ESP8266_SendCommand("AT+CIPCLOSE\r\n", "OK", 1000);
}
// printf重定向到USART2
int fputc(int ch, FILE *f) {
HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 100);
return ch;
}
以下是当前服务器上所有可用的音频文件。点击任意文件可复制其URL。
正在加载音频文件列表...
| 音频名称 | 11025 Hz | 22050 Hz | 44100 Hz |
|---|---|---|---|
| 正在加载... | |||
GET /sound/[filename].wav HTTP/1.1 Host: audio.wuhanqing.cn Connection: close
| 状态码 | 说明 |
|---|---|
| 200 OK | 请求成功,返回音频文件内容 |
| 404 Not Found | 文件不存在 |
[歌曲名]_mono_[采样率].wav 示例: - Sparkle_mono_11025.wav (11.025 kHz) - Sparkle_mono_22050.wav (22.050 kHz) - Sparkle_mono_44100.wav (44.100 kHz)
| 采样率 | 带宽需求 | 音质 | 适用场景 |
|---|---|---|---|
| 11025 Hz | 低 | 基础 | 低速网络、简单提示音 |
| 22050 Hz | 中等 | 良好 | 一般应用、语音播报 |
| 44100 Hz | 高 | 高质量 | 音乐播放、高质量音频 |
# 下载音频文件 curl -O http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav # 查看HTTP头信息 curl -I http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav # 流式下载并播放(需要支持WAV的播放器) curl http://audio.wuhanqing.cn/sound/Sparkle_mono_22050.wav | aplay
如有任何问题、建议或合作需求,欢迎通过以下方式与我联系: