Record audio from a microphone to an SD card?

Oh and one more thing. This is how my SD card module will look like:

Now my questions are:

  • The module has a 5V pin, would be actually safe, for the SD card, input that voltage in there? Or shall I use the 3.3V output of my board?
  • Is the MCU actually fully compatible with a 3.3V logic level for SPI communication? I mean, what if the MOSI pin receives 5V?
  • The pins of the module, are those labeled in pairs or just for a single one?

Thanks beforehand.

I've read in this page how to create the WAV file's header: Wav (RIFF) File Format Tutorial
Now I have even more doubts (talking about the File class in the SD library):

  • I want to "preset" all this header in an array of unsigned bytes (because my only variable is the data-stream length, aka "data chunk"). Which function will put an exact copy of this array into the file? write or print?
  • The size count of the file is updated every time I write (not overwrite) a new byte into it, right?
  • The print function writes an unsigned int variable as a character string or as it is stored in memory?
  • Just in case, is there a way to convert an unsigned int type variable into an array of bytes? (in its "byte form")

Thanks again!

The big problem is there is a disconnect between the pictures and the words. The words say "ARM Arduino" that means a 3V3 one like the Due or the Zero. BUT the pictures show there is a restive divider on the logic lines meaning it is meant to be run with 5V signals.

The module has a 5V pin, would be actually safe, for the SD card, input that voltage in there? Or shall I use the 3.3V output of my board?

It looks like that module has an on board voltage regulator so connect the +5V and do not connect the 3V3.

Is the MCU actually fully compatible with a 3.3V logic level for SPI communication? I mean, what if the MOSI pin receives 5V?

It looks like the module has potential dividers on the pins.

The pins of the module, are those labeled in pairs or just for a single one?

Not at all sure what you mean. Why should anything be in pairs?

Which function will put an exact copy of this array into the file? write or print?

Nether. But write puts the actual byte in the file and print puts an ASCII representation of the value of the byte.

The size count of the file is updated every time I write (not overwrite) a new byte into it, right?

Don't know.

The print function writes an unsigned int variable as a character string

Yes.

is there a way to convert an unsigned int type variable into an array of bytes? (in its "byte form")

Yes

upperByte = variable >> 8;
lowerByte = variable && 0xff;

Grumpy_Mike:
Not at all sure what you mean. Why should anything be in pairs?

Ok, then let me explain you better this time. If you notice, every couple of pins are labeled in an horizontal line. Take a closer look at... let's say, the MOSI label; next to it, there are two pins arranged horizontally. My question is: are those couple of pins connected together? What do you think? (I hope you have understood me this time)

PD: I don't know how to tag (post) a picture, that would be easier for you...

Nether. But write puts the actual byte in the file and print puts an ASCII representation of the value of the byte.

That's fine. I could simply write every single byte by using a for loop. But wait... is there a write function that receives an entire array of bytes? Isn't that the same thing?

upperByte = variable >> 8;

lowerByte = variable && 0xff;

There are some things that I'm worried about.

  • Will that pair of lines just extract two bytes? The int type isn't supposed to have 4 bytes? (aka 32 bits).
  • As the page mentioned before says, I need to fill up two values of 4 bytes each one before I can finalize the file. One is the "RIFF" chunk's length (file size minus 8), and the other one is the "data" chunk's length (file size minus 44). So I need to write down those values byte to byte.
  • I need it in the "little-endian" order (least significant byte first, most significant byte last; in the other way around it is called "big-endian").

I'm asking you all this because I think there is no function in the File class that writes any type of variable as a bunch of bytes (not in a character string form)

My question is: are those couple of pins connected together?

No look at the back, one row are connected to ground. This is normal practice.

is there a write function that receives an entire array of bytes?

Not that I know of.

Will that pair of lines just extract two bytes?

Yes.

The int type isn't supposed to have 4 bytes?

Correct the int type doesn't have four bytes it has two on an Arduino.

I need it in the "little-endian" order

So? I don't see the problem, just write it like that.

Grumpy_Mike:
No look at the back, one row are connected to ground. This is normal practice.

If that is true, then this module will be nearly impossible to place in a "protoboard".

Correct the int type doesn't have four bytes it has two on an Arduino.

All right then. Let me see if I'm correct:

  • The byte type has... well, one byte... duh.
  • The short type has ? (unknown amount?) bytes.
  • The int type has two bytes, as you told me.
  • The long type has... 8 bytes I guess?
  • The float type has... 4 bytes?
  • I guess the double type doesn't exist in the Arduino IDE right?

So what can I do? I need an unsigned integer of 4 bytes. :confused:

Long int has four bytes, a long long has eight.

Grumpy_Mike:
Long int has four bytes, a long long has eight.

So, what's the keyword? (on the Arduino IDE of course)
"int" or "long int"?

or

https://learn.sparkfun.com/tutorials/data-types-in-arduino

All right, so the unsigned long type is what I need. I don't think a 8 KHz sampling rate can be disrupted by long-type variable operations.
Now the thing is: how to extract every single byte from that variable, and in what order they will be extracted? I'll take care of the "byte order" part...

Same as reply #22 only you do a bit more shifting.

byte0 = value & 0xff;
byte1 = (value >> 8) & 0xff;
byte2 = (value >> 16) & 0xff;
byte3 = (value >> 24) & 0xff;

Lucario448:
By the way, how you can achieve a higher sampling rate?

If you're willing to go with 32 bit ARM, the Teensy audio library has working 44.1 kHz play and record, and a lot more.

This 48 minute tutorial video shows many of the features.

However, recording the SD card isn't show in the video. But you can find the example code here:

If you really want to stay on 8 bit AVR, you're probably going to have to settle for lower quality sound. Moving so much data around on a slow chip without DMA is really hard. On a faster 32 bit chip with an optimized library that uses DMA to efficiently bring the audio in and out, it's much easier.

I don't know what happened, but I thought I've replied that last post. I've said that the board looks awesome, and already acknowledged that with a AVR MCU I can't expect a recording sampling rate higher than 8 KHz; but, I wanna do this with what I have on hand. And also wondering if by writing data directly to the SD card there would be a jitter in the sampling rate.

On the other hand, I made this sketch for my audio recorder, compilable as it is, for now it is not well documented; but I hope that the code is self-explanatory.
I'm open to hear any suggestion, improvement, optimization, etc.; that I should make to this code. If you think that a comment is redundant or irrelevant, then just ignore it (again, it is not well documented yet).

Now here is the code:

/*
  SD card attached to SPI bus as follows:
** MOSI - pin 11
** MISO - pin 12
** CLK - pin 13
** CS - pin 10
*/

#include <SPI.h>
#include <SD.h>

PROGMEM const byte header [44] =
 // This contains the header of a WAV file. Without this, it is not recognized as such.
 // 44 bytes long.
{
 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
 0x45, 0x66, 0x6D, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
 0x01, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x40, 0x1F, 0x00, 0x00, 0x01,
 0x00, 0x08, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};

const byte firstPos = 4;
// Position in the file to write down the overall file size minus 8.
const byte secondPos = 40;
// Position in the file to write down the overall file size minus 44.
const byte button = 2; // Pin for the button.
const byte led = 4;
// Pin for the status LED. Unfortunately, the built-in LED is already
// used by the SPI.
byte buttonState = LOW;
// Remember the button state before test the loop's condition. LOW is default or
// not pressed.
File recording;

void setup() {

 pinMode(button, INPUT);
 pinMode(led, OUTPUT);
 pinMode(10, OUTPUT);
 ADCSRA = (ADCSRA & 0xf8) | 0x04; // ADC fast mode
 digitalWrite(led, LOW);
 if (!SD.begin(10)) {
   errorStatus(); // SD card failure
 }
 if (SD.exists("REC001.wav")) {
   if (!SD.remove("REC001.wav")) {
     errorStatus();
     // Any previous (or incomplete) recording should be deleted
     // in order to make a new one. This error might occur due to
     // write-protected or damaged card.
   }
 }
}

void loop() {
 if (digitalRead(button) == HIGH) {
   recording = SD.open("REC001.wav", FILE_WRITE);
   if (recording) {
     writeHeader();
     standby();
     buttonState = LOW;
     record();
     finalize();
     finishStatus();
   } else {
     errorStatus(); // Might be write-protected card or else
   }
 }
}

void writeHeader() {
 // Puts the "idetification" of a typical WAV file (because .wav isn't enough)
 for (byte pos = 0; pos < sizeof(header); pos++) {
   recording.write(pgm_read_byte(&header[pos]));
 }
}

void record() {
 /*
  * This is how the recording process works. This will last until (normally)
  * the press of the button.
  * 
  * DO NOT RESET OR POWER-down THE BOARD WHILE IN THIS STATE, UNTIL THE STATUS LED
  * GETS IN THE FINISHED STATE; otherwise file corruption and/or SD card damage will occur.
  * 
  * Exceeding 4 GiB file size (149hr 7min 50sec of audio recording), when meeting a file system's
  * limitation, or filling up the memory; may cause unexpected behavior.
  */
 byte sample = 0;
 digitalWrite(led, HIGH);
 while (buttonState == LOW) {
   sample = analogRead(A0) >> 2;
   recording.write(sample);
   buttonState = digitalRead(button);
   delayMicroseconds(0);
   // Make a trial an error test to know the amount of delay
 }
 digitalWrite(led, LOW);
}

void finalize() {
 // Fills up two necessary variables in the file's header,
 // then ensures a successfully saved recording.
 byte finalValue[4];
 unsigned long fileSize = recording.size();
 unsigned long riffSize = fileSize - 8;
 unsigned long dataSize = fileSize - 44;

 finalValue[0] = riffSize & 0xff;
 finalValue[1] = (riffSize >> 8) & 0xff;
 finalValue[2] = (riffSize >> 16) & 0xff;
 finalValue[3] = (riffSize >> 24) & 0xff;
 // Is possible to make a fuction that returns an array of bytes?

 recording.seek(firstPos);
 recording.write(finalValue, 4);
 // Check if already in little-endian order

 finalValue[0] = dataSize & 0xff;
 finalValue[1] = (dataSize >> 8) & 0xff;
 finalValue[2] = (dataSize >> 16) & 0xff;
 finalValue[3] = (dataSize >> 24) & 0xff;
 // Is possible to make a fuction that returns an array of bytes?

 recording.seek(secondPos);
 recording.write(finalValue, 4);
 // Check if already in little-endian order
 recording.close();
}

void errorStatus() {
 // The status LED endlessly indicates a critical error.
 // The only way out, is by resetting the MCU (reset button or power down).
 while (true) {
   digitalWrite(led, HIGH);
   delay(250);
   digitalWrite(led, LOW);
   delay(250);
 }
}


void finishStatus() {
/* The status LED endlessly indicates a successfully saved recording.
 * The only way out, is by resetting the MCU (reset button or power down).
 * 
 * From here, it is safe to reset or power-down the board.
 * 
 * If there is a way to generate filenames automatically, then this function
 * will become useless.
 */
 while (true) {
   digitalWrite(led, HIGH);
   delay(1000);
   digitalWrite(led, LOW);
   delay(1000);
 }
}

void standby() {
 // The status LED endlessly indicates that the MCU is ready to record
 // (until you press the button)
 while (true) {
   digitalWrite(led, HIGH);
   if (digitalRead(button) == HIGH) {
     break;
   }
   delay(500);
   digitalWrite(led, LOW);
   if (digitalRead(button) == HIGH) {
     break;
   }
   delay(500);
 }
}

Thanks, the board is quasi developmental, quasi datalogger, whatever you might find a SD card & RTC & RS232 & dual hardware serial ports with lots of SRAM (16K, twice that of a Mega) useful for. I wired one up to prove out a standalone programmer because it had the right parts - 3.3V buffered SD card and 400mA 3.3V regulator. 150mA from an Uno/Mega is not enough to power an SD card.
I think fat16lib had the code running on a 328P board also. The fast ADC and DAC are what allow the higher sampling rate. 8 or 10 bits at lower rates don't sound that good for audio. I was going for electronic drums eventually, will get there at some point. Need to re-visit the trigger and peak detect circuit I had working back on 1990 before we bought a house and moved and started doing all that homeowner stuff.