Introduction: Arduino (Mega) Audio Recording

Record Audio to your Audino Mega SD card.

The audio file can be played back on a standard audio application or analysed byte by byte.

This Instructable will show you how audio input can be repeatedly added to a 512 byte buffer and then transferred to a SD card in real time. The period recorded can be altered.

The sample rate is 9.4 KHz and the wav file output 8 bit, mono. Whilst not hi-fidelity, the sound quality is perfectly adequate.

The recorded wav file can be saved as tabulated data. It can also be displayed as a simple scrolling graph on the monitor.

All files are time stamped using a unix time code sent from the serial monitor.

The inspiration for this article came from reading Amanda Ghassaei: https://www.instructables.com/id/Arduino-Audio-Inp...

.

My latest program update at the end of this instructable, increases the sample rate to 19 KHz with a significant improvement in audio quality.

.

You may also be interested in my instructables on:

A high speed Arduino Oscilloscope: https://www.instructables.com/id/Arduino-High-speed...

Playing wav files from the Arduino: https://www.instructables.com/id/Arduino-playing-wa...

.

Inevitably SD card technology has already improved. I have tested with a Sandisk Ultra 8GB sd card. This is significantly faster than the Kingston 4GB card I started with. Using my updated software I am able to record at 38.3KHz, with no degradation in quality. (4/8/2014)

Step 1: Requirements

Arduino Mega 2560

The following components work- alternatives may be viable (with program tweaking- I leave that to you!)

LCD Keypad Shield http://www.hobbytronics.co.uk/arduino-lcd-keypad-...

MicroSD Breakout Board Regulated with Logic Conversion V2

http://www.hobbytronics.co.uk/microsd-card-regula...

4GB Micro SD Memory Card http://www.hobbytronics.co.uk/microsd-card-regula...

Microphone pre-amplifier-

ac coupled with a potential divider to centre the voltage between Arduino 0-5 V rail

Amanda Ghassaei has published a circuit at https://www.instructables.com/id/Arduino-Audio-Inpu...

I designed my own with bass, treble and volume controls. However there are plenty of pre-amp designs on the web.

Step 2: The Specifications

Serial, LCD, SD and flash memory support.

Serial set at 115200 baud.

Time stamp in Linux format from Serial monitor.

Serial Input can initiate new data capture- using the command 'again' .

Serial input can set the number of Kbytes of data read and therefore the duration of the audio file- using the command 'alter' .

Serial output of data after input request- command 'read' - output can be stopped with command 'q'

Basic graph plotted on serial monitor.

Raw data saved in wav compatible file.

Raw data translated to tabulated file- command 'write'.

Repeated 60 s time update to eeprom. LCD dims during time update to eeprom.

PWM 3 Square wave available for testing- toggle with command 'test'.

Input 2 set high avoids wait for serial time set.

Uno use may be possible with program tweaking including the the relocation of the time storage bytes (variable ememory). I leave that work to you.

NB best quality audio is obtained with the input varying around the mid voltage value of 128. This is important!

Lcd output of basic information.

Lcd adc port specified with variable lcdport.

Lcd button support:

Select initiates a new sample Left alters Sample Kbyte number- then up +10, down -10, left -1, right +1

Step 3: Getting Started

Connect the LCD keypad shield.

Connect the SD card reader

0, 5V

CLK to pin 52

D0 to pin 50

D1 to pin 51

CS to pin 53

I mounted the SD reader on a bit of strip board with pass through pins to match the required connection points.

(Details to follow)

You will probably want to test the lcd and sd card reader using sample scripts from the point of purchase.

This will sound a little casual, but it is all detailed else-where:

Build a microphone pre-amp and connect it through a potential divider- as described by Amanda.

Or use another design.

I recommend that you include a variable resistor in the potential divider section so that the input can be carefully adjusted to centre around a measured input value of 128 (Half of the 0-5 supply voltage. On my PC "5V" is less than 5V when powered by the USB.)

The lcd screen I specified uses analogue port zero for the lcd buttons- so connect your audio input to A1.

Step 4: Mounting the Sdcard Reader

The sdcard can be easily mounted on a piece of strip board and located using pass through pins.

I used two sets of pin connectors (top and bottom), to stabilize the board.

The connections are next to each other on the Arduino Mega. So the copper strip board tracks between the connections must be cut. I used a hand held junior hack saw blade (not in the saw frame) with one locating pin knocked out. Make sure you use a good quality fine toothed blade. It is also very useful for cleaning the gap between the copper strips. I found it easier to cut the 5V track whilst cutting the next two. (See picture 4) It is then a simple matter to solder a wire across the cut.

For the pins I used standard pin strip. The pin length on the Copper side was adjusted to the depth of the Arduino socket, prior to soldering. Spare pin length on the component side was cut and filed clean following soldering.

An eight way header connecter was soldered on and a 90 degree pin connecter plugged in. A small piece of fine dowel was used to support the reader. A small hole drilled through the corner of the board allows a screw to hold the dowel, although glue would be ok.

I added a pin header to the 0V line on the strip board. This ensured that I had not lost the availability of the 0V connection point.

The SDcard reader will need a soldered 90 degree socket header.

The 5V power supply can be collected with a flying lead to one of the pins between 21 and 22 (marked 5V).

Check that you have cut all the tracks shown in the picture, including the tracks cut with a drill bit!

Check your layout and soldering before plugging in!

Step 5: Wave File Format

A wave file header is a 44 byte block.

The header is at the start of the wav file.

The header block must be initialised globally with:

byte wavheader[44];

Audio data follows the header.

For an 8 bit, mono wav file data is a number between 0 and 255.

The script reads the A1 port, interpreting the incoming audio as a voltage between 0 and 255. Perfect!

Only 4 sections of the header need to be updated when the a wav file is saved.

These are shown as the commented sections in the picture.

The bytes at offset 4 contain the length of the data + the length of the header - 2*4 bytes

Or more simply the data count +36.

Each commented section is a 4 byte number.

The four bytes occur with the least significant byte first- this is "little endian format".

The following routine will accept a long value and write it to a specified point in the wav header as 4 succesive bytes:

void headmod(long value, byte location){
// write four bytes for a long

tempfile.seek(location); // find the location in the file

byte tbuf[4];

tbuf[0] = value & 0xFF; // lo byte

tbuf[1] = (value >> 8) & 0xFF;

tbuf[2] = (value >> 16) & 0xFF;

tbuf[3] = (value >> 24) & 0xFF; // hi byte

tempfile.write(tbuf,4); // write the 4 byte buffer

}

Step 6: Setting the Interrupt That Reads the Analogue Port

We can set up an interrupt so that one analogue port is repeatedly read.

(See Amanda's article for further details)

My "startad" subroutine uses a pre-scalar of 128.

This creates a repeated interrupt so that port A1 is continually read at 9.4KHz.

The interrupt subroutine has a two fold function:

1) If the counter is less than the number of readings:

  • Add the audio data byte to the 512 byte buffer.
  • when the buffer is full, write the buffer in one go to the sdcard- 512 bytes is the optimal size for fast data saves. Reset the buffer count to zero for the next incoming audio byte.

2) When the counter is equal to the required number of readings:

Stop the interrupt occurring again

  • Calculate how long the process took, frequency etc.
  • Use the frequency and file size to update the wave file header.
  • Release analogue port A1 and enable port A0 so that the lcd buttons will work.

Step 7: Setting the Date and Time and Sneaky Saving

The Arduino can keep track of time, but has to be told a starting point.

When it is restarted- it has to be told again. (Unless you fit a real time clock).

The time can be specified as a unix time string- which is the number of seconds elapsed since the " standard epoch of 1/1/1970"

The serial console can be used to send a T followed by the unix time string.

T1403524800 represents 12am June 3rd 2014

See subroutine "waitfordate" and "processSyncMessage"

if (Serial.find(TIME_HEADER)) { // Look for the T
pctime = Serial.parseInt(); // extract the time

if( pctime >= DEFAULT_TIME) { // limited check that the time is after default_time

setTime(pctime); // Sync Arduino clock to the time received on the serial port

This site offers unix time values: http://www.onlineconversion.com/unix_time.htm

Time values can be stored in eeprom memory, which is retained after reset.

The values stored are historic- they are not updated during reset.

However they are useful as a starting point for new filenames. When the script is run without time-stamping the previous time value is used with a 60s addition.

writeeeprom and readeeprom allow the storage and retrieval of time strings.

The time is written to the eeprom memory every 60 seconds.

Be aware that eeprom memory has a re-use limit - but the board sockets will probably wear out first.

Step 8: What Is the Output and How Is It Controlled

A folder called adlog is used for the data.

The following output is available:

1) A wav file for each read. The filename is in the format ddhhmmss.wav Audio capture can be repeated with the command "again".

2) Following the serial console command "write", a text file is produced with the data number and data value in columns. This is comma delimited and can readily be imported into other programs for graphical analysis. The filename format is ddhhmmss.txt

3) The serial console command "read" produces a vertically scrolling graphical representation of the audio. This can be stopped by sending a "q" from the console.

The lcd buttons can also be used to record a new audio file and alter the number of logged bytes. Select starts a new file and left starts the "select" function which updates the data number. The lcd A0 port is read and depending on whether the up, down, left or right button is pressed the data number is altered.

The serial console command "test" generates a square wave on PWM3. This is useful for testing without a microphone and pre-amp..

If PWM2 is taken high, the program will not wait for a timestamp from the serial console. The date and time will no longer be current. However this is useful if the usb is not connected.

Step 9: Program Update

I have successfully increased the data acquisition frequency to 19 KHz.

This has entailed using two 512byte buffers (double buffering) and a rewrite of the interrupt and void loop sections.

The audio is very much cleaner.

I also tested at 38 Khz, with very promising results. However at this frequency occasional buffer wait periods are present. It is probable that future improvements in SDCard design and the SD library function speed, will overcome this issue. For those that want to experiment alter the prescalar variable just before void setup.

I have set up bufa and bufb.

In the interrupt routine I set a flag called aready - it is true when writing to bufa, false for bufb.

The flag writeit is true when a write is required and set false when the SdCard finishes.

When a buffer is full (buffcount==BUF_SIZE):

  • I check if the Sdcard is finished writing, in which case writeit==false and I reset the buffer pointer back to zero, switch the buffer flag aready and set the writeit flag true.
  • If the Sdcard is still writing I go one reading back (bufcount--; and counter--;) and exit the interrupt.

Once I have the correct number of readings I shut down the interrupt, write the last data block and tidy up.

The majority of the data is written in void loop:

if(writeit){ // Data is ready to be written

if (aready){

tempfile.write(bufb, BUF_SIZE); // write the data block from bufb

} else {

// initiate block write from bufa

tempfile.write(bufa, BUF_SIZE); // write the data block

}

writeit=false; // flag that the write is done

}