Skip to content

Instantly share code, notes, and snippets.

@mongonta0716
Created June 29, 2025 17:28
Show Gist options
  • Save mongonta0716/2a1355f066202d3396259de94bb07611 to your computer and use it in GitHub Desktop.
Save mongonta0716/2a1355f066202d3396259de94bb07611 to your computer and use it in GitHub Desktop.
M5Dial + PDMUnit Sample(Port.B)
#include <M5Unified.h>
#include <SPIFFS.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#define SAMPLE_RATE 8000 // Sampling rate
#define CHUNK 1024 // I2S buffer size
#define PERIAD 10 // Recording time (sec)
#define CONFIG_I2S_LRCK_PIN 1 // I2S clock
#define CONFIG_I2S_DATA_IN_PIN 2 // I2S data
// WiFi settings
const char* ssid = "YOUR_SSID"; // Change to your WiFi SSID
const char* password = "YOUR_PASSWORD"; // Change to your WiFi password
// WebServer settings
WebServer server(80);
// --- WAV header function prototypes ---
void writeWavHeader(File& file, int sampleRate);
void updateWavHeader(File& file);
void button_callback(){
M5_LOGI("Start recording...");
M5.Lcd.setCursor(0, 0);
M5.Lcd.fillScreen(BLACK);
M5.Lcd.println("Start recording...");
M5.Lcd.println("Recording for "+String(PERIAD)+" seconds");
File f = SPIFFS.open("/audio.wav", FILE_WRITE);
if (f) {
// Write WAV header
writeWavHeader(f, SAMPLE_RATE);
uint8_t buffer[CHUNK];
size_t byte_read;
// Sampling rate * 16bit * 1ch * recording time (bits)
// /8 (bytes)
// /CHUNK (number of chunks)
int chunk_max = (int)((SAMPLE_RATE*16*1*PERIAD)/8/CHUNK);
M5.Lcd.printf("Total chunks: %d\n", chunk_max);
unsigned long startTime = millis();
int progressLine = 40; // LCD position for progress
for(int i=0; i < chunk_max; i++){
M5.Mic.record(buffer, CHUNK, SAMPLE_RATE, false);
f.write(buffer, CHUNK);
// Show progress every 10 chunks
if (i % 10 == 0) {
M5.Lcd.setCursor(0, progressLine);
M5.Lcd.printf("Progress: %d/%d (%.1f%%)", i, chunk_max, (float)i*100/chunk_max);
M5.Display.print(".");
}
}
// Get file size and update header
updateWavHeader(f);
f.close();
unsigned long duration = (millis() - startTime) / 1000;
M5.Lcd.setCursor(0, progressLine + 20);
M5.Lcd.printf("Actual recording time: %lu seconds\n", duration);
M5.Lcd.println("\nFile saved as /audio.wav");
M5.Lcd.println("Access via: http://" + WiFi.localIP().toString());
} else {
M5.Lcd.println("Failed to open file for writing");
}
M5.Lcd.println("End recording...");
delay(2000); // Keep the end message visible for 2 seconds
}
// Function to write WAV header
void writeWavHeader(File& file, int sampleRate) {
byte header[44];
unsigned int fileSize = 0;
// RIFF header
header[0] = 'R';
header[1] = 'I';
header[2] = 'F';
header[3] = 'F';
// File size (to be updated later)
header[4] = 0;
header[5] = 0;
header[6] = 0;
header[7] = 0;
// WAVE format
header[8] = 'W';
header[9] = 'A';
header[10] = 'V';
header[11] = 'E';
// fmt chunk
header[12] = 'f';
header[13] = 'm';
header[14] = 't';
header[15] = ' ';
// fmt chunk size (16 bytes)
header[16] = 16;
header[17] = 0;
header[18] = 0;
header[19] = 0;
// Audio format (1 = PCM)
header[20] = 1;
header[21] = 0;
// Number of channels (1 = mono)
header[22] = 1;
header[23] = 0;
// Sampling rate
header[24] = sampleRate & 0xFF;
header[25] = (sampleRate >> 8) & 0xFF;
header[26] = (sampleRate >> 16) & 0xFF;
header[27] = (sampleRate >> 24) & 0xFF;
// Byte rate (sampling rate * channels * bytes per sample)
unsigned int byteRate = sampleRate * 1 * 2;
header[28] = byteRate & 0xFF;
header[29] = (byteRate >> 8) & 0xFF;
header[30] = (byteRate >> 16) & 0xFF;
header[31] = (byteRate >> 24) & 0xFF;
// Block align (channels * bytes per sample)
header[32] = 2;
header[33] = 0;
// Bit depth (16 bits)
header[34] = 16;
header[35] = 0;
// data chunk
header[36] = 'd';
header[37] = 'a';
header[38] = 't';
header[39] = 'a';
// Data size (to be updated later)
header[40] = 0;
header[41] = 0;
header[42] = 0;
header[43] = 0;
file.write(header, 44);
}
// Function to update WAV header with file size
void updateWavHeader(File& file) {
unsigned int fileSize = file.size();
unsigned int dataSize = fileSize - 44;
// Move to the beginning of the file
file.seek(0);
// Update RIFF chunk size (file size - 8)
byte fileSizeBytes[4];
fileSizeBytes[0] = (fileSize - 8) & 0xFF;
fileSizeBytes[1] = ((fileSize - 8) >> 8) & 0xFF;
fileSizeBytes[2] = ((fileSize - 8) >> 16) & 0xFF;
fileSizeBytes[3] = ((fileSize - 8) >> 24) & 0xFF;
file.seek(4);
file.write(fileSizeBytes, 4);
// Update data chunk size
byte dataSizeBytes[4];
dataSizeBytes[0] = dataSize & 0xFF;
dataSizeBytes[1] = (dataSize >> 8) & 0xFF;
dataSizeBytes[2] = (dataSize >> 16) & 0xFF;
dataSizeBytes[3] = (dataSize >> 24) & 0xFF;
file.seek(40);
file.write(dataSizeBytes, 4);
}
// Function to handle HTTP server root page
void handleRoot() {
String html = "<html><body>";
html += "<h1>M5Dial PDM Unit Audio Recorder</h1>";
html += "<p>Click below to download the recorded audio:</p>";
html += "<p><a href='/download'>Download WAV File</a></p>";
html += "</body></html>";
server.send(200, "text/html", html);
}
// Function to handle WAV file download
void handleDownload() {
File file = SPIFFS.open("/audio.wav", FILE_READ);
if (file) {
server.sendHeader("Content-Disposition", "attachment; filename=audio.wav");
server.sendHeader("Connection", "close");
server.streamFile(file, "audio/wav");
file.close();
} else {
server.send(404, "text/plain", "File not found");
}
}
// Function to connect to WiFi
void connectToWiFi() {
M5.Lcd.println("Connecting to WiFi...");
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
M5.Lcd.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
M5.Lcd.println();
M5.Lcd.println("WiFi connected!");
M5.Lcd.print("IP address: ");
M5.Lcd.println(WiFi.localIP());
// mDNS setup
if (MDNS.begin("m5dial")) {
M5.Lcd.println("mDNS responder started");
M5.Lcd.println("You can access at: http://m5dial.local");
}
} else {
M5.Lcd.println();
M5.Lcd.println("WiFi connection failed!");
}
}
void setup() {
auto cfg = M5.config();
cfg.internal_mic = false;
M5.begin(cfg);
M5.Lcd.setBrightness(200);
M5.Lcd.fillScreen(BLACK);
auto mic_config = M5.Mic.config();
mic_config.pin_ws = CONFIG_I2S_LRCK_PIN; // I2Sクロック
mic_config.pin_data_in = CONFIG_I2S_DATA_IN_PIN; // I2S
mic_config.sample_rate = SAMPLE_RATE; // サンプリングレート
M5.Mic.config(mic_config);
// Initialize SPIFFS
if(!SPIFFS.begin(true)){
M5.Lcd.println("SPIFFS Mount Failed");
return;
}
// Connect to WiFi
connectToWiFi();
// Set up web server routes
server.on("/", handleRoot);
server.on("/download", handleDownload);
// Start server
server.begin();
M5.Lcd.println("HTTP server started");
M5.Lcd.println("\nPress button A to start recording");
M5.Lcd.println("Recording time set to " + String(PERIAD) + " seconds");
}
void loop() {
M5.update();
if (M5.BtnA.wasClicked()) {
button_callback();
}
// HTTPリクエストを処理
server.handleClient();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment