这段代码展示了不使用第三方库实现在一个 HTTP Server, 然后电脑可以通过浏览器打开 http://ip/stream 即可听到 IIS 麦克风拾取到的声音。实验使用MSM261S4030H0的 IIS 音频传感器。
#include <WiFi.h>
#include <WebServer.h>
#include <driver/i2s.h>
// WiFi配置
const char* ssid = "YOURSSID";
const char* password = "YOURPASSWORD";
// I2S配置
#define I2S_MIC_WS 37
#define I2S_MIC_SD 48
#define I2S_MIC_SCK 45
#define SAMPLE_RATE 16000
#define BITS_PER_SAMPLE 32
#define BUFFER_SIZE 1024
WebServer server(80);
int32_t audioBuffer[BUFFER_SIZE];
void setup() {
Serial.begin(2000000);
// 初始化I2S
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = BUFFER_SIZE,
.use_apll = false
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_MIC_SCK,
.ws_io_num = I2S_MIC_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = I2S_MIC_SD
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.println("IP address: " + WiFi.localIP().toString());
// 设置HTTP路由
server.on("/stream", HTTP_GET, handleAudioStream);
server.begin();
}
void loop() {
server.handleClient();
// 持续读取音频数据
size_t bytesRead;
i2s_read(I2S_NUM_0, &audioBuffer, BUFFER_SIZE * sizeof(int32_t), &bytesRead, portMAX_DELAY);
}
void handleAudioStream() {
// 生成WAV文件头
uint8_t wavHeader[44];
generateWavHeader(wavHeader, SAMPLE_RATE, 32);
// 发送HTTP头
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "audio/wav", "");
// 先发送WAV头
server.sendContent((const char*)wavHeader, sizeof(wavHeader));
// 持续发送音频数据
while(server.client().connected()) {
size_t bytesRead;
i2s_read(I2S_NUM_0, &audioBuffer, BUFFER_SIZE*4, &bytesRead, portMAX_DELAY);
server.sendContent((const char*)audioBuffer, bytesRead);
}
}
void generateWavHeader(uint8_t* header, uint32_t sampleRate, uint32_t bitDepth) {
// RIFF块标识
header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
// 文件总大小占位(后续更新)
header[4] = 0; header[5] = 0; header[6] = 0; header[7] = 0;
// WAVE格式标识
header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';
// fmt子块标识
header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';
// fmt块大小(16字节)
header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0;
// 音频格式(1=PCM)
header[20] = 1; header[21] = 0;
// 声道数(1=单声道)
header[22] = 1; header[23] = 0;
// 采样率
header[24] = sampleRate & 0xFF;
header[25] = (sampleRate >> 8) & 0xFF;
header[26] = (sampleRate >> 16) & 0xFF;
header[27] = (sampleRate >> 24) & 0xFF;
// 字节率 = 采样率 * 声道数 * 位深度/8
uint32_t byteRate = sampleRate * (bitDepth/8);
header[28] = byteRate & 0xFF;
header[29] = (byteRate >> 8) & 0xFF;
header[30] = (byteRate >> 16) & 0xFF;
header[31] = (byteRate >> 24) & 0xFF;
// 块对齐 = 声道数 * 位深度/8
uint16_t blockAlign = (bitDepth/8);
header[32] = blockAlign & 0xFF;
header[33] = (blockAlign >> 8) & 0xFF;
// 位深度
header[34] = bitDepth & 0xFF;
header[35] = (bitDepth >> 8) & 0xFF;
// data子块标识
header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
// data块大小占位(后续更新)
header[40] = 0; header[41] = 0; header[42] = 0; header[43] = 0;
}
类似的,有时候传感器送出 32Bits 数据,我们需要转为 16Bits输出,编写代码如下:
#include <WiFi.h>
#include <WebServer.h>
#include <driver/i2s.h>
// WiFi配置
const char* ssid = "CMCC-TSR6739";
const char* password = "!!1783az";
// I2S配置
#define I2S_MIC_WS 37
#define I2S_MIC_SD 48
#define I2S_MIC_SCK 45
#define SAMPLE_RATE 16000
#define BITS_PER_SAMPLE 32
#define BUFFER_SIZE 1024
WebServer server(80);
int32_t audioBuffer[BUFFER_SIZE];
int16_t webaudioBuffer[BUFFER_SIZE];
void setup() {
Serial.begin(2000000);
// 初始化I2S
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = BUFFER_SIZE,
.use_apll = false
};
i2s_pin_config_t pin_config = {
.bck_io_num = I2S_MIC_SCK,
.ws_io_num = I2S_MIC_WS,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = I2S_MIC_SD
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
// 连接WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi connected");
Serial.println("IP address: " + WiFi.localIP().toString());
// 设置HTTP路由
server.on("/stream", HTTP_GET, handleAudioStream);
server.begin();
}
void loop() {
server.handleClient();
// 持续读取音频数据
size_t bytesRead;
i2s_read(I2S_NUM_0, &audioBuffer, BUFFER_SIZE * sizeof(int32_t), &bytesRead, portMAX_DELAY);
}
void handleAudioStream() {
// 生成WAV文件头
uint8_t wavHeader[44];
generateWavHeader(wavHeader, SAMPLE_RATE, 16);
// 发送HTTP头
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "audio/wav", "");
// 先发送WAV头
server.sendContent((const char*)wavHeader, sizeof(wavHeader));
// 持续发送音频数据
while(server.client().connected()) {
size_t bytesRead;
i2s_read(I2S_NUM_0, &audioBuffer, BUFFER_SIZE*4, &bytesRead, portMAX_DELAY);
for (int i=0;i<BUFFER_SIZE;i++) {
int32_t audio_24bit = audioBuffer[i] >> 8; // 提取高 24 位
webaudioBuffer[i]=(int16_t)((audio_24bit + 128) >> 8); // 四舍五入到 16 位
}
server.sendContent((const char*)webaudioBuffer, bytesRead/2);
}
}
void generateWavHeader(uint8_t* header, uint32_t sampleRate, uint32_t bitDepth) {
// RIFF块标识
header[0] = 'R'; header[1] = 'I'; header[2] = 'F'; header[3] = 'F';
// 文件总大小占位(后续更新)
header[4] = 0; header[5] = 0; header[6] = 0; header[7] = 0;
// WAVE格式标识
header[8] = 'W'; header[9] = 'A'; header[10] = 'V'; header[11] = 'E';
// fmt子块标识
header[12] = 'f'; header[13] = 'm'; header[14] = 't'; header[15] = ' ';
// fmt块大小(16字节)
header[16] = 16; header[17] = 0; header[18] = 0; header[19] = 0;
// 音频格式(1=PCM)
header[20] = 1; header[21] = 0;
// 声道数(1=单声道)
header[22] = 1; header[23] = 0;
// 采样率
header[24] = sampleRate & 0xFF;
header[25] = (sampleRate >> 8) & 0xFF;
header[26] = (sampleRate >> 16) & 0xFF;
header[27] = (sampleRate >> 24) & 0xFF;
// 字节率 = 采样率 * 声道数 * 位深度/8
uint32_t byteRate = sampleRate * (bitDepth/8);
header[28] = byteRate & 0xFF;
header[29] = (byteRate >> 8) & 0xFF;
header[30] = (byteRate >> 16) & 0xFF;
header[31] = (byteRate >> 24) & 0xFF;
// 块对齐 = 声道数 * 位深度/8
uint16_t blockAlign = (bitDepth/8);
header[32] = blockAlign & 0xFF;
header[33] = (blockAlign >> 8) & 0xFF;
// 位深度
header[34] = bitDepth & 0xFF;
header[35] = (bitDepth >> 8) & 0xFF;
// data子块标识
header[36] = 'd'; header[37] = 'a'; header[38] = 't'; header[39] = 'a';
// data块大小占位(后续更新)
header[40] = 0; header[41] = 0; header[42] = 0; header[43] = 0;
}