How to record sound using 18032 and 18003 with sd

Hardware: 18032(ultrasonic) 18003(interposer with FPC and SD) 19007 (esp32 WiFi)
here is my code

/**
   @file RecordToSD.ino
   @author rakwireless.com
   @brief This example shows how to record sound to SD card.
   First, you need to prepare the hardware module and SD card.
    Step 1: Compile and download the example to RAK11200.
    Part 2: Reset RAK11200 and open the serial port.
    Part 3: When the serial port prints out "Recording started", the microphone starts to collect sound,
            and the default sampling time is 8 seconds.
    Step 4: When the serial port prints "Recording finished", the microphone completes the recording and
            stores the audio data to the SD card.
    Part 5: Insert the SD card into the player to play the WAV format audio file you have recorded,
            the default file name is "record.wav".
   @note This example need use the microphone module and RAK18003 module.
   @version 0.1
   @date 2022-06-6
   @copyright Copyright (c) 2022
*/

#include "audio.h"
#include <driver/i2s.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include "PDM.h"
#include <string.h>

File new_audio_file;
// default number of output channels
static const int channels = 1;
// default PCM output frequency
static const int frequency = 96000; // 48000 have also tried.
#define BIT_PER_SAMPLE 16
#define RECORD_TIME    30    //recording time default 8 seconds

TPT29555   Expander1(0x23);
TPT29555   Expander2(0x25);

void RAK18003Init(void);
void microphone_record(const char* song_name, uint32_t duration);
bool create_wav_file(const char* song_name, uint32_t duration, uint16_t num_channels, const uint32_t sampling_rate, uint16_t bits_per_sample);
void SD_CS_low();
void SD_CS_high();
void generate_file_name(const char *dirname, char file_name[]);

void setup() {

  pinMode(WB_IO2, OUTPUT);
  digitalWrite(WB_IO2, HIGH);
  delay(500);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_BLUE, OUTPUT);
  digitalWrite(LED_BLUE, LOW);
  digitalWrite(LED_GREEN, LOW);

  // Initialize Serial for debug output
  time_t timeout = millis();
  Serial.begin(115200);
  while (!Serial)
  {
    if ((millis() - timeout) < 1000)
    {
      delay(100);
    }
    else
    {
      break;
    }
  }

  Serial.println("==========Record To SD example============");
  RAK18003Init();

  digitalWrite(LED_BLUE, LOW);
  digitalWrite(LED_GREEN, LOW);

  SD_CS_low();  //enable the SPI CS
  if (!SD.begin(SS, SPI, 8000000, "/sd", 5)) { // Start access to the SD.
    Serial.println("Card Mount Failed");
  }

  
  if (SD.exists("/record.wav")) {
    Serial.println("Remove file first!");
    SD.remove("/record.wav");
  }

  char file_name[200] = "";
  generate_file_name("/", file_name);

  const char* song_name = file_name;

  // initialize PDM with:
  if (!PDM.begin(channels, frequency)) {
    Serial.println("Failed to start PDM!");
    while (1) yield();
  }

  microphone_record(song_name, RECORD_TIME);    //the default record file name is "record.wav".
  SD_CS_high(); //disable the SPI CS
}
void loop()
{

}
void RAK18003Init(void)
{
  while (!Expander1.begin())
  {
    Serial.println("Did not find RAK18003 IO Expander Chip1,please check!");
    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_GREEN, HIGH);
    delay(200);
    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_GREEN, LOW);
    delay(200);
  }

  while (!Expander2.begin())
  {
    Serial.println("Did not find RAK18003 IO Expander Chip2,please check!");
    digitalWrite(LED_BLUE, HIGH);
    digitalWrite(LED_GREEN, HIGH);
    delay(200);
    digitalWrite(LED_BLUE, LOW);
    digitalWrite(LED_GREEN, LOW);
    delay(200);
  }
  Expander1.pinMode(0, INPUT);    //SD check
  Expander1.pinMode(1, INPUT);    //MIC check
  Expander1.pinMode(2, INPUT);   //MIC CTR1
  Expander1.pinMode(3, INPUT);   //MIC CTR2
  Expander1.pinMode(4, INPUT);    //AMP check
  Expander1.pinMode(5, INPUT);   //AMP CTR1
  Expander1.pinMode(6, INPUT);   //AMP CTR2
  Expander1.pinMode(7, INPUT);   //AMP CTR3
  Expander1.pinMode(8, INPUT);    //DSP check
  Expander1.pinMode(9, INPUT);    //DSP CTR1  DSP int
  Expander1.pinMode(10, INPUT);   //DSP CTR2  DSP ready
  Expander1.pinMode(11, INPUT);  //DSP CTR3  DSP reset
  Expander1.pinMode(12, INPUT);  //DSP CTR4  not use
  Expander1.pinMode(13, INPUT);  //DSP CTR5  not use
  Expander1.pinMode(14, INPUT);  //NOT USE
  Expander1.pinMode(15, INPUT);  //NOT USE

  Expander2.pinMode(0, OUTPUT);  //CORE  SPI CS1   DSP CS
  Expander2.pinMode(1, OUTPUT);  //CORE  SPI CS2
  Expander2.pinMode(2, OUTPUT);  //CORE  SPI CS3
  Expander2.pinMode(3, OUTPUT);  //PDM switch CTR    1 to dsp   0 to core
  Expander2.pinMode(4, OUTPUT);  //not use
  Expander2.pinMode(5, OUTPUT);  //not use
  Expander2.pinMode(6, OUTPUT);  //not use
  Expander2.pinMode(7, OUTPUT);  //not use
  Expander2.pinMode(8, OUTPUT);  //not use
  Expander2.pinMode(9, OUTPUT);  //not use
  Expander2.pinMode(10, OUTPUT); //not use
  Expander2.pinMode(11, OUTPUT); //not use
  Expander2.pinMode(12, OUTPUT); //not use
  Expander2.pinMode(13, OUTPUT); //not use
  Expander2.pinMode(14, OUTPUT); //not use
  Expander2.pinMode(15, OUTPUT); //not use

  Expander2.digitalWrite(3, 0);   //set the PDM data direction from MIC to WisCore

  while (Expander1.digitalRead(1) == 0) //Check if the microphone board is connected on the RAK18003
  {
    Serial.println("There is no microphone, please check !");
    // Serial.println(Expander1.readAllPin());
    // Serial.println(Expander1.digitalRead(1));
    // Serial.println(Expander1.readReg(1));
    delay(500);
  }
   while(Expander1.digitalRead(0) == 1)  //Check SD card
   {
     Serial.println("There is no SD card on the RAK18003 board, please check !");
     delay(500);
   }
}
void SD_CS_high()
{
  Expander2.digitalWrite(2, 1);
}
void SD_CS_low()
{
  Expander2.digitalWrite(2, 0);
}
// Create a file and add wav header to it so we can play it from PC later
bool create_wav_file(const char* song_name, uint32_t duration, uint16_t num_channels, const uint32_t sampling_rate, uint16_t bits_per_sample) {
  // data size in bytes - > this amount of data should be recorded from microphone later
  uint32_t data_size = sampling_rate * num_channels * bits_per_sample * duration / 8;

  new_audio_file = SD.open(song_name, FILE_WRITE);
  if (new_audio_file == NULL) {
    Serial.println("Failed to create file");
    return false;
  }

  /* *************** ADD ".WAV" HEADER *************** */
  uint8_t CHUNK_ID[4] = {'R', 'I', 'F', 'F'};
  new_audio_file.write(CHUNK_ID, 4);

  uint32_t chunk_size = data_size + 36;
  uint8_t CHUNK_SIZE[4] = {chunk_size, chunk_size >> 8, chunk_size >> 16, chunk_size >> 24};
  new_audio_file.write(CHUNK_SIZE, 4);

  uint8_t FORMAT[4] = {'W', 'A', 'V', 'E'};
  new_audio_file.write(FORMAT, 4);

  uint8_t SUBCHUNK_1_ID[4] = {'f', 'm', 't', ' '};
  new_audio_file.write(SUBCHUNK_1_ID, 4);

  uint8_t SUBCHUNK_1_SIZE[4] = {0x10, 0x00, 0x00, 0x00};
  new_audio_file.write(SUBCHUNK_1_SIZE, 4);

  uint8_t AUDIO_FORMAT[2] = {0x01, 0x00};
  new_audio_file.write(AUDIO_FORMAT, 2);

  uint8_t NUM_CHANNELS[2] = {num_channels, num_channels >> 8};
  new_audio_file.write(NUM_CHANNELS, 2);

  uint8_t SAMPLING_RATE[4] = {sampling_rate, sampling_rate >> 8, sampling_rate >> 16, sampling_rate >> 24};
  new_audio_file.write(SAMPLING_RATE, 4);

  uint32_t byte_rate = num_channels * sampling_rate * bits_per_sample / 8;
  uint8_t BYTE_RATE[4] = {byte_rate, byte_rate >> 8, byte_rate >> 16, byte_rate >> 24};
  new_audio_file.write(BYTE_RATE, 4);

  uint16_t block_align = num_channels * bits_per_sample / 8;
  uint8_t BLOCK_ALIGN[2] = {block_align, block_align >> 8};
  new_audio_file.write(BLOCK_ALIGN, 2);

  uint8_t BITS_PER_SAMPLE[2] = {bits_per_sample, bits_per_sample >> 8};
  new_audio_file.write(BITS_PER_SAMPLE, 2);

  uint8_t SUBCHUNK_2_ID[4] = {'d', 'a', 't', 'a'};
  new_audio_file.write(SUBCHUNK_2_ID, 4);

  uint8_t SUBCHUNK_2_SIZE[4] = {data_size, data_size >> 8, data_size >> 16, data_size >> 24};
  new_audio_file.write(SUBCHUNK_2_SIZE, 4);

  // Actual data should be appended after this point later
  new_audio_file.close();
  return true;
}
void microphone_record(const char* song_name, uint32_t duration) {

  // Add wav header to the file so we can play it from PC later
  //  if (!create_wav_file(song_name, duration, channels, frequency, BIT_PER_SAMPLE)) {
  //    Serial.println("Error during wav header creation");
  //    return;
  //  }
  //Extract mono data from stereo
  if (!create_wav_file(song_name, duration, 1, frequency, BIT_PER_SAMPLE)) {
    Serial.println("Error during wav header creation");
    return;
  }
  // Buffer to receive data from microphone
  const size_t fifo_size = 512;
  uint8_t* buf = (uint8_t*)malloc(fifo_size);

  // Open created .wav file in append+binary mode to add PCM data
  File audio_file = SD.open(song_name, FILE_APPEND);
  if (audio_file == NULL) {
    Serial.println("Failed to create file");
    return;
  }
  // data size in bytes - > this amount of data should be recorded from microphone
  uint32_t data_size = frequency * BIT_PER_SAMPLE * duration / 8;

  // Record until "file_size" bytes have been read from mic.
  uint32_t counter = 0;
  digitalWrite(LED_BLUE, HIGH);
  digitalWrite(LED_GREEN, HIGH);
  Serial.println("Recording started");
  while (counter != data_size) {
    // Check for file size overflow
    if (counter > data_size) {
      Serial.println("File is corrupted. data_size must be multiple of fifo_size. Please modify fifo_size");
      break;
    }
    // Read data from microphone
    int bytes_read = PDM.read(buf, fifo_size);
    if (bytes_read > 0)
    {
      for (int i = 0; i < fifo_size; i++)
      {
        buf[i] = buf[i] << 1; //2x the original volume
        // Serial.print("Buf: ");
        // Serial.println(buf[i]);
      }
    }

    if (channels == 2)
    {
      const size_t write_size = bytes_read >> 1;
      uint8_t* write_buf = (uint8_t*)malloc(write_size);
      for (int i = 0; i < (write_size >> 1); i++) //Convert stereo data to mono
      {
        write_buf[i * 2] = buf[i * 4];
        write_buf[i * 2 + 1] = buf[i * 4 + 1];
      }
      // Save data to SD card
      audio_file.write(write_buf, write_size);
      // Increment the counter
      counter += write_size;
      free(write_buf);
    }
    else
    {
      audio_file.write(buf, fifo_size);
      // Increment the counter
      counter += fifo_size;
    }
  }
  digitalWrite(LED_BLUE, LOW);
  digitalWrite(LED_GREEN, LOW);
  Serial.println("Recording finished");
  audio_file.close();
  PDM.end();
  free(buf);
}

void generate_file_name(const char *dirname, char file_name[])
{
  File root = SD.open(dirname);
  if (!root) {
    Serial.println("Failed to open directory");
    return;
  }
  if (!root.isDirectory()) {
    Serial.println("Not a directory");
    return;
  }

  File file;
  int idx = 0;
  while (file = root.openNextFile())
  {
    if (!file.isDirectory())
    {
      const char* name = file.name();
      Serial.println(file.name());
      char* _name = strdup(name);
      char* token = strtok(_name, "_.");
      Serial.println(token);
      while (token != nullptr)
      {
        token = strtok(NULL,"_.");
        if (isdigit(*token))
        {
          int _idx = atoi(token);
          Serial.print("_idx = ");
          Serial.println(_idx);
          if (_idx > idx)
          {
            idx = _idx;            
          }
          break;
        }
        Serial.println(token);
      }     
    }
  }
  idx++;
  Serial.print("idx = ");
  Serial.println(idx);
  // char file_name[100];
  sprintf(file_name,"/record_%d.wav",idx);

  Serial.println(file_name);
}

However, the recorded wav file does not output correct sound. It seems to have no sound at all. When I navigate to other timestamp, it has current noise. Can anyone tell me what is wrong?

Hi @ethanlyu ,

I did a verification and the RecordToSD.ino example works ok.

Few things to check:

  1. RAKwireless Audio Library and RAK11200 BSP must be the latest.
  2. Verify that the RAK18032, RAK18003 and WisBlock Core+Base are working fine by running first PDMSerialPlotterFFT_RAK18003.ino example. FFT output should be visible on Serial Plotter of Arduino IDE 1.8.x. This ensure that it is not hardware related issue.
  3. .Try to reformat the SD card if possible or try a different SD card.