Created
June 29, 2025 17:28
-
-
Save mongonta0716/2a1355f066202d3396259de94bb07611 to your computer and use it in GitHub Desktop.
M5Dial + PDMUnit Sample(Port.B)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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