Last active
July 31, 2025 21:22
-
-
Save SimedruF/81008a320bb3d195e7acda29e71c40b6 to your computer and use it in GitHub Desktop.
ESp32_record_audio_with_INM441.cpp
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
/* | |
Florin Simedru | |
Complete project details at https://automatic-house.blogspot.com | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files. | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
*/ | |
#include "FS.h" | |
#include "SD.h" | |
#include "SPI.h" | |
#include <driver/i2s.h> | |
void i2s_adc(void *arg); | |
void i2sInit(); | |
void i2s_adc_data_scale(uint8_t * d_buff, uint8_t* s_buff, uint32_t len); | |
void example_disp_buf(uint8_t* buf, int length); | |
void wavHeader(byte* header, int wavSize); | |
void listSPIFFS(void); | |
void listDir(fs::FS &fs, const char * dirname, uint8_t levels); | |
void createDir(fs::FS &fs, const char * path); | |
void removeDir(fs::FS &fs, const char * path); | |
void readFile(fs::FS &fs, const char * path); | |
void writeFile(fs::FS &fs, const char * path, const char * message); | |
void appendFile(fs::FS &fs, const char * path, const char * message); | |
void renameFile(fs::FS &fs, const char * path1, const char * path2); | |
void deleteFile(fs::FS &fs, const char * path); | |
void testFileIO(fs::FS &fs, const char * path); | |
void SD_Audio_File_Init(fs::FS &fs,const char * path, const char * message); | |
#define I2S_WS 15 | |
#define I2S_SD 13 | |
#define I2S_SCK 2 | |
#define I2S_PORT I2S_NUM_0 | |
#define I2S_SAMPLE_RATE (16000) | |
#define I2S_SAMPLE_BITS (16) | |
#define I2S_READ_LEN (16 * 1024) | |
#define RECORD_TIME (20) //Seconds | |
#define I2S_CHANNEL_NUM (1) | |
#define FLASH_RECORD_SIZE (I2S_CHANNEL_NUM * I2S_SAMPLE_RATE * I2S_SAMPLE_BITS / 8 * RECORD_TIME) | |
File file_audio; | |
File file; | |
File sd_root; | |
const char filename[] = "/recording.wav"; | |
const int headerSize = 44; | |
void listDir(fs::FS &fs, const char * dirname, uint8_t levels){ | |
Serial.printf("Listing directory: %s\n", dirname); | |
File root = fs.open(dirname); | |
if(!root){ | |
Serial.println("Failed to open directory"); | |
return; | |
} | |
if(!root.isDirectory()){ | |
Serial.println("Not a directory"); | |
return; | |
} | |
File file = root.openNextFile(); | |
while(file){ | |
if(file.isDirectory()){ | |
Serial.print(" DIR : "); | |
Serial.println(file.name()); | |
if(levels){ | |
listDir(fs, file.name(), levels -1); | |
} | |
} else { | |
Serial.print(" FILE: "); | |
Serial.print(file.name()); | |
Serial.print(" SIZE: "); | |
Serial.println(file.size()); | |
} | |
file = root.openNextFile(); | |
} | |
} | |
void createDir(fs::FS &fs, const char * path){ | |
Serial.printf("Creating Dir: %s\n", path); | |
if(fs.mkdir(path)){ | |
Serial.println("Dir created"); | |
} else { | |
Serial.println("mkdir failed"); | |
} | |
} | |
void removeDir(fs::FS &fs, const char * path){ | |
Serial.printf("Removing Dir: %s\n", path); | |
if(fs.rmdir(path)){ | |
Serial.println("Dir removed"); | |
} else { | |
Serial.println("rmdir failed"); | |
} | |
} | |
void readFile(fs::FS &fs, const char * path){ | |
Serial.printf("Reading file: %s\n", path); | |
File file = fs.open(path); | |
if(!file){ | |
Serial.println("Failed to open file for reading"); | |
return; | |
} | |
Serial.print("Read from file: "); | |
while(file.available()){ | |
Serial.write(file.read()); | |
} | |
file.close(); | |
} | |
void writeFile(fs::FS &fs, const char * path, const char * message){ | |
Serial.printf("Writing file: %s\n", path); | |
File file = fs.open(path, FILE_WRITE); | |
if(!file){ | |
Serial.println("Failed to open file for writing"); | |
return; | |
} | |
if(file.print(message)){ | |
Serial.println("File written"); | |
} else { | |
Serial.println("Write failed"); | |
} | |
file.close(); | |
} | |
void appendFile(fs::FS &fs, const char * path, const char * message){ | |
Serial.printf("Appending to file: %s\n", path); | |
File file = fs.open(path, FILE_APPEND); | |
if(!file){ | |
Serial.println("Failed to open file for appending"); | |
return; | |
} | |
if(file.print(message)){ | |
Serial.println("Message appended"); | |
} else { | |
Serial.println("Append failed"); | |
} | |
file.close(); | |
} | |
void renameFile(fs::FS &fs, const char * path1, const char * path2){ | |
Serial.printf("Renaming file %s to %s\n", path1, path2); | |
if (fs.rename(path1, path2)) { | |
Serial.println("File renamed"); | |
} else { | |
Serial.println("Rename failed"); | |
} | |
} | |
void deleteFile(fs::FS &fs, const char * path){ | |
Serial.printf("Deleting file: %s\n", path); | |
if(fs.remove(path)){ | |
Serial.println("File deleted"); | |
} else { | |
Serial.println("Delete failed"); | |
} | |
} | |
void testFileIO(fs::FS &fs, const char * path){ | |
File file = fs.open(path); | |
static uint8_t buf[512]; | |
size_t len = 0; | |
uint32_t start = millis(); | |
uint32_t end = start; | |
if(file){ | |
len = file.size(); | |
size_t flen = len; | |
start = millis(); | |
while(len){ | |
size_t toRead = len; | |
if(toRead > 512){ | |
toRead = 512; | |
} | |
file.read(buf, toRead); | |
len -= toRead; | |
} | |
end = millis() - start; | |
Serial.printf("%u bytes read for %u ms\n", flen, end); | |
file.close(); | |
} else { | |
Serial.println("Failed to open file for reading"); | |
} | |
file = fs.open(path, FILE_WRITE); | |
if(!file){ | |
Serial.println("Failed to open file for writing"); | |
return; | |
} | |
size_t i; | |
start = millis(); | |
for(i=0; i<2048; i++){ | |
file.write(buf, 512); | |
} | |
end = millis() - start; | |
Serial.printf("%u bytes written for %u ms\n", 2048 * 512, end); | |
file.close(); | |
} | |
void SD_Audio_File_Init(fs::FS &fs,const char * path, const char * message) | |
{ | |
Serial.printf("Writing file: %s\n", path); | |
file_audio = fs.open(filename, FILE_WRITE); | |
if(!file_audio){ | |
Serial.println("Failed to open file for writing"); | |
return; | |
} | |
byte header[headerSize]; | |
wavHeader(header, FLASH_RECORD_SIZE); | |
file_audio.write(header, headerSize); | |
listDir(SD, "/", 0); | |
} | |
/* setup */ | |
void setup(){ | |
Serial.begin(115200); | |
/* I2S initialization*/ | |
i2sInit(); | |
/* Create task for i2s - i2s_adc */ | |
xTaskCreate(i2s_adc, "i2s_adc", 1024 * 16, NULL, 1, NULL); | |
/* SD card initialization */ | |
if(!SD.begin(5)) | |
{ | |
Serial.println("Card Mount Failed"); | |
return; | |
} | |
uint8_t cardType = SD.cardType(); | |
if(cardType == CARD_NONE){ | |
Serial.println("No SD card attached"); | |
return; | |
} | |
/* select the card type*/ | |
Serial.print("SD Card Type: "); | |
if(cardType == CARD_MMC){ | |
Serial.println("MMC"); | |
} else if(cardType == CARD_SD){ | |
Serial.println("SDSC"); | |
} else if(cardType == CARD_SDHC){ | |
Serial.println("SDHC"); | |
} else { | |
Serial.println("UNKNOWN"); | |
} | |
uint64_t cardSize = SD.cardSize() / (1024 * 1024); | |
Serial.printf("SD Card Size: %lluMB\n", cardSize); | |
/* Test SD card */ | |
listDir(SD, "/", 0); | |
createDir(SD, "/mydir"); | |
listDir(SD, "/", 0); | |
removeDir(SD, "/mydir"); | |
listDir(SD, "/", 2); | |
writeFile(SD, "/hello.txt", "Hello "); | |
appendFile(SD, "/hello.txt", "World!\n"); | |
readFile(SD, "/hello.txt"); | |
deleteFile(SD, "/foo.txt"); | |
renameFile(SD, "/hello.txt", "/foo.txt"); | |
readFile(SD, "/foo.txt"); | |
testFileIO(SD, "/test.txt"); | |
/* initialization of the audion file */ | |
SD_Audio_File_Init(SD,"/",filename); | |
Serial.printf("Total space: %lluMB\n", SD.totalBytes() / (1024 * 1024)); | |
Serial.printf("Used space: %lluMB\n", SD.usedBytes() / (1024 * 1024)); | |
} | |
void loop(){ | |
} | |
/* print sound data */ | |
void example_disp_buf(uint8_t* buf, int length) | |
{ | |
printf("======\n"); | |
for (int i = 0; i < length; i++) { | |
printf("%02x ", buf[i]); | |
if ((i + 1) % 8 == 0) { | |
printf("\n"); | |
} | |
} | |
printf("======\n"); | |
} | |
/* I2S initialization pins and structures */ | |
void i2sInit(){ | |
esp_err_t err; | |
i2s_config_t i2s_config = { | |
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), | |
.sample_rate = I2S_SAMPLE_RATE, | |
.bits_per_sample = i2s_bits_per_sample_t(I2S_SAMPLE_BITS), | |
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, | |
.communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), | |
.intr_alloc_flags = 0, | |
.dma_buf_count = 64, | |
.dma_buf_len = 1024, | |
.use_apll = 1 | |
}; | |
i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); | |
const i2s_pin_config_t pin_config = { | |
.bck_io_num = I2S_SCK, | |
.ws_io_num = I2S_WS, | |
.data_out_num = -1, | |
.data_in_num = I2S_SD | |
}; | |
// Configuring the I2S driver and pins. | |
// This function must be called before any I2S driver read/write operations. | |
err = i2s_driver_install(I2S_PORT, &i2s_config, 0, NULL); | |
if (err != ESP_OK) { | |
Serial.printf("Failed installing driver: %d\n", err); | |
while (true); | |
} | |
err = i2s_set_pin(I2S_PORT, &pin_config); | |
if (err != ESP_OK) { | |
Serial.printf("Failed setting pin: %d\n", err); | |
while (true); | |
} | |
Serial.println("I2S driver installed."); | |
} | |
void i2s_adc_data_scale(uint8_t * d_buff, uint8_t* s_buff, uint32_t len) | |
{ | |
uint32_t j = 0; | |
uint32_t dac_value = 0; | |
for (int i = 0; i < len; i += 2) { | |
dac_value = ((((uint16_t) (s_buff[i + 1] & 0xf) << 8) | ((s_buff[i + 0])))); | |
d_buff[j++] = 0; | |
d_buff[j++] = dac_value * 256 / 2048; | |
} | |
} | |
/*I2S task for sound recording*/ | |
void i2s_adc(void *arg) | |
{ | |
int i2s_read_len = I2S_READ_LEN; | |
int flash_wr_size = 0; | |
size_t bytes_read; | |
char* i2s_read_buff = (char*) calloc(i2s_read_len, sizeof(char)); | |
uint8_t* flash_write_buff = (uint8_t*) calloc(i2s_read_len, sizeof(char)); | |
i2s_read(I2S_PORT, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY); | |
i2s_read(I2S_PORT, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY); | |
Serial.println(" *** Recording Start *** "); | |
while (flash_wr_size < FLASH_RECORD_SIZE) { | |
//read data from I2S bus, in this case, from ADC. | |
i2s_read(I2S_PORT, (void*) i2s_read_buff, i2s_read_len, &bytes_read, portMAX_DELAY); | |
//example_disp_buf((uint8_t*) i2s_read_buff, 64); | |
//save original data from I2S(ADC) into flash. | |
i2s_adc_data_scale(flash_write_buff, (uint8_t*)i2s_read_buff, i2s_read_len); | |
file_audio.write((const byte*) flash_write_buff, i2s_read_len); | |
flash_wr_size += i2s_read_len; | |
ets_printf("Sound recording %u%%\n", flash_wr_size * 100 / FLASH_RECORD_SIZE); | |
ets_printf("Never Used Stack Size: %u\n", uxTaskGetStackHighWaterMark(NULL)); | |
} | |
file_audio.close(); | |
free(i2s_read_buff); | |
i2s_read_buff = NULL; | |
free(flash_write_buff); | |
flash_write_buff = NULL; | |
listDir(SD, "/", 0); | |
vTaskDelete(NULL); | |
} | |
/* Setup the WAV header*/ | |
void wavHeader(byte* header, int wavSize){ | |
header[0] = 'R'; | |
header[1] = 'I'; | |
header[2] = 'F'; | |
header[3] = 'F'; | |
unsigned int fileSize = wavSize + headerSize - 8; | |
header[4] = (byte)(fileSize & 0xFF); | |
header[5] = (byte)((fileSize >> 8) & 0xFF); | |
header[6] = (byte)((fileSize >> 16) & 0xFF); | |
header[7] = (byte)((fileSize >> 24) & 0xFF); | |
header[8] = 'W'; | |
header[9] = 'A'; | |
header[10] = 'V'; | |
header[11] = 'E'; | |
header[12] = 'f'; | |
header[13] = 'm'; | |
header[14] = 't'; | |
header[15] = ' '; | |
header[16] = 0x10; | |
header[17] = 0x00; | |
header[18] = 0x00; | |
header[19] = 0x00; | |
header[20] = 0x01; | |
header[21] = 0x00; | |
header[22] = 0x01; | |
header[23] = 0x00; | |
header[24] = 0x80; | |
header[25] = 0x3E; | |
header[26] = 0x00; | |
header[27] = 0x00; | |
header[28] = 0x00; | |
header[29] = 0x7D; | |
header[30] = 0x00; | |
header[31] = 0x00; | |
header[32] = 0x02; | |
header[33] = 0x00; | |
header[34] = 0x10; | |
header[35] = 0x00; | |
header[36] = 'd'; | |
header[37] = 'a'; | |
header[38] = 't'; | |
header[39] = 'a'; | |
header[40] = (byte)(wavSize & 0xFF); | |
header[41] = (byte)((wavSize >> 8) & 0xFF); | |
header[42] = (byte)((wavSize >> 16) & 0xFF); | |
header[43] = (byte)((wavSize >> 24) & 0xFF); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment