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: http://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: http://www.instructables.com/id/Arduino-High-speed...

Playing wav files from the Arduino: http://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


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 http://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.

<p>Sir,</p><p>Do I need exactly Mega 2560 or I can use an Ordinary ATMEGA328 on a veroboard? I'm asking this because ATMEGA2560 offers same speed and it is also 8-bit, but offers a few extra pins. If I can use an ordinary ATMEGA328 uC then it will be great. If it can be done without problem, please mention it and also tell me something about the modifications.</p>
<p>Hi Pinakig,</p><p>sorry I have no advise to offer on that setup.</p><p>I use Megas for all of my Arduino applications.</p><p>Cheers,</p><p>David</p>
<p>Hi , im new to arduino , i need to record wav to SD card without the LCD , can you please modify the code , for the arduino due.</p><p>any help will be appreciated .</p>
<p>Please re-phrase with a specific question about the article as published.</p>
<p>Hi David!<br>Thanks a lot for this instructable!</p><p>I am planning on building the circuit but want to use a Winbond SPI flash (W25Q64 - 64Mbit) instead of the SD card.</p><p>If I am not mistaken then the buffer size for the Winbond should probably be 256bytes instead of the 512bytes.</p><p>Did you ever play around with flash chips?</p>
<p>Hi,</p><p>I haven't used an SPI flash device.</p><p>I cannot think of a downside in reducing the buffer size to 256.</p><p>If the flash write cycle is faster than the sdcard write time (512bytes in 1.6 ms)- you should be able to easily achieve 38KHz. (Assuming you adapt my double buffering code.).</p><p>What is the write-rate for your flash?</p><p>Let me know what happens.</p><p>David</p>
<p>Science marches on!</p><p>I tested the latest Sandisk extreme 16GB card today: (On special offer at Tesco)</p><p>512 bytes are read in 1.56 mS. That is 25% faster than the previous card (2.07mS)</p><p>The sound quality is very good!</p>
<p>Hi David,</p><p>This is an awesome article, thanks for posting. </p><p>I have recently started working with arduino UNO and the &quot;write&quot; function in sd Card library is not working. Below is my code. I have tried with &quot;println&quot; which is working fine. Any help regarding this is welcome.</p><p>void loop()</p><p>{</p><p> byte b=10;</p><p> File dataFile = SD.open(&quot;datalog.txt&quot;, FILE_WRITE);</p><p> if (dataFile) </p><p> {</p><p> dataFile.write(b);</p><p> }</p><p>} </p>
<p>Hello Nareshkumar,</p><p>Check you have the latest version of the SD library installed.</p><p>I wrote the following code to test your problem.</p><p>It works if you send byte 10 as a single variable b, or send 10 as the first byte in buffer bufa.</p><p>I presume you are testing how to send bytes, not strings? The suffix &quot;.txt&quot; in your filename was confusing.</p><p><br>#include &lt;SD.h&gt;<br>// set up variables using the SD utility library functions:<br><br>const int defaultselect= 10; // uno (mega is 53)<br>const int chipselect = 10; // any pin you want, including default<br><br>File datafile;<br>Sd2Card card;<br><br>void setup(){<br>Serial.begin(115200); // start serial for output<br>Serial.flush();<br>// setup 2 buffers, any size you might need<br>uint8_t bufa[20];<br>uint8_t bufb[20];<br>// initilise buffers<br>bufa[0]=10;<br>bufb[0]=0;<br>byte b =10;<br>Serial.print(&quot;bufa[0] &quot;);Serial.println(bufa[0]);<br>Serial.print(&quot;bufb[0] &quot;);Serial.println(bufb[0]);<br>// make sure default select line is set to output<br><br> if (chipselect != defaultselect) { pinMode(defaultselect, OUTPUT); } <br> <br> if (SD.begin(chipselect)) {<br> card.init(SPI_FULL_SPEED, chipselect);<br> Serial.println(&quot;Card works&quot;);<br> // delete old file<br> if (SD.exists(&quot;datalog.txt&quot;)) SD.remove(&quot;datalog.txt&quot;);<br> // write the byte<br> File datafile = SD.open(&quot;datalog.txt&quot;, FILE_WRITE);<br> if (datafile){<br><br> // comment out one at a time to test<br> datafile.write(bufa,1);<br> //datafile.write(b);<br> // ___________________________________________<br> datafile.close();<br> Serial.println(&quot;Byte written!&quot;);<br> // try to read the byte back<br> datafile = SD.open(&quot;datalog.txt&quot;, FILE_READ);<br> if (!datafile){<br> Serial.println(&quot;File failed to open for read&quot;);<br> } else{<br> datafile.read(bufb,1);<br> datafile.close();<br> Serial.print(&quot;Byte returned &quot;);<br> Serial.println(bufb[0]);<br> } <br> } else{<br> Serial.println(&quot;File failed to open for write&quot;);<br> }<br> } else{<br> Serial.println(&quot;Card failed to initialise&quot;);<br> }<br>}<br><br>void loop(){<br>}</p><p>David</p>
<p>I have tried the above code but still I am unable to &quot;<strong>write&quot; </strong>to the sd card. I am using arduino 1.0.6 and the SD library is up to date as shown in the image below. I am trying to use your code to capture audio and I am unable to do with <strong>write function </strong>of SD library; when I am using for loops and then printing each byte of the buffer arrays to the SD card, the frequency is decreasing to 3 KHz as opposed to 9 KHz . The code of yours I am using is pasted below. Is there any way to overcome this? Thanks!</p><strong>Code:</strong><p>#include &lt;SD.h&gt;</p><p>#include &lt;SPI.h&gt;</p><p>// set up variables using the SD utility library functions:</p><p>Sd2Card card;</p><p>SdVolume volume;</p><p>SdFile root;</p><p>const int chipSelect = 10; // (Or any suitable pin!)</p><p>File tempfile;</p><p>boolean hascard=false;</p><p>boolean written, aready, writeit;</p><p>unsigned long starttime , endtime, filesize;</p><p>float frequency;</p><p>float period , interval;</p><p>int const adport = 2; // Set the port to be used for input!</p><p>// buf is used to store icoming data and is written to file when full</p><p>// 512 bytes is optimized for sdcard</p><p>#define BUF_SIZE 128</p><p>int bufa[BUF_SIZE];//uint8_t bufa[BUF_SIZE];</p><p>int bufb[BUF_SIZE];//uint8_t bufb[BUF_SIZE];</p><p>uint16_t bufcount;</p><p>char bufFile[]=&quot;/adlog/00112233.txt&quot;;</p><p>unsigned long readings = 100000; // initial sample size- kept small to avoid delay- enough to create data to look at</p><p>int i;</p><p>unsigned long counter;</p><p>// Defines for clearing register bits</p><p>#ifndef cbi</p><p>#define cbi(sfr, bit) (_SFR_BYTE(sfr) &amp;= ~_BV(bit))</p><p>#endif</p><p>// Defines for setting register bits</p><p>#ifndef sbi</p><p>#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))</p><p>#endif</p><p>// alter prescalar here 32, 64, 128</p><p>// 32 is the fastest sampling rate</p><p>byte prescalar=128;</p><p>void setup(){</p><p> //Specimem frequency used for first reading to estimate initial sample duration</p><p> /* if(prescalar==32) frequency = 38.3829;</p><p> if(prescalar==64) frequency= 19.2300;</p><p> if(prescalar==128) frequency= 9.6182;*/</p><p> pinMode(10, OUTPUT); </p><p>Serial.begin(115200); // start serial for output</p><p>Serial.flush();</p><p>if (SD.begin(chipSelect)) {</p><p> hascard=true;</p><p> aready=true;</p><p> card.init(SPI_FULL_SPEED, chipSelect);</p><p> // Create adlog sub-directory</p><p> if (SD.exists(&quot;/adlog&quot;) == false) {</p><p> SD.mkdir(&quot;/adlog&quot;);</p><p> }</p><p> hascard=fileopen();</p><p> }</p><p>cli();</p><p>startad();</p><p>}</p><p>void startad(){</p><p>// Setup continuous reading of the adc port 'adport' using an interrupt</p><p>cli();//disable interrupts</p><p>//clear ADCSRA and ADCSRB registers</p><p>ADCSRA = 0;</p><p>ADCSRB = 0;</p><p>ADMUX |= adport; //set up continuous sampling of analog pin adport </p><p>//ADMUX |= (1 &lt;&lt; REFS0); //set reference voltage</p><p>ADMUX |= (1 &lt;&lt; ADLAR); //left align the ADC value- so we can read highest 8 bits from ADCH register only</p><p>// pre scalar to set interrupt frequency:</p><p>//ADCSRA |= (1 &lt;&lt; ADPS2); // 16 prescalar 72Khz, but what could you do at that rate?</p><p>// 128 prescalar - 9.4 Khz sampling</p><p>if (prescalar==128) ADCSRA |= (1 &lt;&lt; ADPS2) | (1 &lt;&lt; ADPS1) | (1 &lt;&lt; ADPS0);</p><p>//32 prescaler - 16mHz/32=500kHz - produces 37 Khz sampling</p><p>if (prescalar==32) ADCSRA |= (1 &lt;&lt; ADPS2) | (1 &lt;&lt; ADPS0);</p><p>// 64 prescalar produces 19.2 Khz sampling</p><p>if(prescalar==64) ADCSRA |= (1 &lt;&lt; ADPS2) | (1 &lt;&lt; ADPS1);</p><p>ADCSRA |= (1 &lt;&lt; ADATE); //enable auto trigger</p><p>ADCSRA |= (1 &lt;&lt; ADIE); </p><p>//ADCSRA |= (1 &lt;&lt; ADEN); //enable ADC- works fine, but so does the next line</p><p>sbi(ADCSRA,ADEN); // //enable ADC</p><p>ADCSRA |= (1 &lt;&lt; ADSC); //start ADC measurements on interrupt</p><p>writeit=false;</p><p>starttime=millis();</p><p>sei();//enable interrupts</p><p>} </p><p>// Interrupt routine *******************************************</p><p>// this is the key to the program!!</p><p>ISR(ADC_vect) {</p><p> if (counter &lt; readings) {</p><p> if(aready) { // get the new value from analogue port</p><p> bufa[bufcount]=ADCH;</p><p> } else {</p><p> bufb[bufcount]=ADCH;</p><p> }</p><p> counter++; // increment data counter</p><p> bufcount++; // increment buffer counter</p><p> if(bufcount==BUF_SIZE){</p><p> if(writeit==false) {</p><p> bufcount=0;</p><p> aready = ! aready;</p><p> writeit=true; // flag that a write is needed</p><p> } else { // wait for file write to complete</p><p> bufcount--;</p><p> counter--;</p><p> }</p><p> }</p><p> } else {</p><p> // All data collected</p><p> cli();//disable interrupts</p><p> cbi(ADCSRA,ADEN); // disable ADC</p><p> sei();//enable interrupts</p><p> endtime=millis();</p><p> // write the last block</p><p> if (aready){</p><p> for(int i=0;i&lt;BUF_SIZE;i++)</p><p> {</p><p> tempfile.println(bufa[i]); </p><p> }/// write the data block</p><p> // tempfile.write(bufa, BUF_SIZE); </p><p> } else {</p><p> for(int i=0;i&lt;BUF_SIZE;i++)</p><p> {</p><p> tempfile.println(bufb[i]); </p><p> }</p><p> // initiate block write from B</p><p> //tempfile.write(bufb, BUF_SIZE); // write the data block</p><p> }</p><p> Serial.println(F(&quot;Logging stopped&quot;));</p><p> period= endtime-starttime;</p><p> frequency = float(readings)/period;</p><p> Serial.println(frequency);</p><p> interval = 1000/frequency;</p><p> tempfile.flush();</p><p> }</p><p>}</p><p>// End Interupt section *******************************************</p><p>void loop(){</p><p> if(writeit){</p><p> if (aready){</p><p> for(int i=0;i&lt;BUF_SIZE;i++)</p><p> {</p><p> tempfile.println(bufb[i]); </p><p> } </p><p> //tempfile.write(bufb, BUF_SIZE); // write the data block</p><p> } else {</p><p> for(int i=0;i&lt;BUF_SIZE;i++)</p><p> {</p><p> tempfile.println(bufa[i]); </p><p> }</p><p> // initiate block write from buf A</p><p> //tempfile.write(bufa, BUF_SIZE); // write the data block</p><p> }</p><p> writeit=false; // the write is done!</p><p> }</p><p>}</p><p>// End Void Loop section ******************************************</p><p>boolean fileopen(){</p><p> if (SD.exists(bufFile)) {SD.remove(bufFile);}</p><p>tempfile = SD.open(bufFile, FILE_WRITE | O_TRUNC);</p><p> if (!tempfile) {</p><p> Serial.println(F(&quot;Tempfile failed to open&quot;));</p><p> return(false);</p><p> } else{</p><p> // hasdata=false;</p><p> written=false;</p><p> counter=0;</p><p> bufcount=0;</p><p> float var = float(readings)/1000;</p><p> float estimate = var / frequency;</p><p> Serial.print(F(&quot;\nLogging (&quot;));</p><p> Serial.print(estimate,2);</p><p> Serial.println(F(&quot; S)&quot;));</p><p> return(true);</p><p> }</p><p>}</p>
<p>Hi,</p><p>What happened when you tried the write one byte example?</p><p>What output appeared in the serial monitor?</p><p>The Uno runs at a lower frequency than the mega and has considerably less memory. Both these factors are working against you!</p>
<p>Hi,</p><p>In the one byte example,the text file gets created but the byte doesn't get printed to the text file. I can't rely on the serial monitor output because you are directly printing <b>bufb[0] </b>in the end<b>,</b> by only checking whether the text file has been created. Thanks!</p>
<p>Hi,</p><p>You can rely on the serial output:</p><p>bufb(0) is initialised with zero</p><p>bufb(0) is read from the file in the line </p><p>datafile.read(bufb,1);</p><p>a one byte buffer is read into the array bufb!</p><p>.</p><p>If bufb(0) is still zero, then the file is in error.</p><p>It would be helpful to supply a screen capture from your serial monitor after each of the test routine options have executed.</p>
<p>Hi,</p><p>I have understood the issue; number 10 happens to be new line character in ASCII. So, I was unable to view the character in the text file. When I try with different numbers their respective ASCII values are getting printed. When we are reading them in an arduino code they are again being converted to their respective human understandable values. Thanks for your help for the whole day. Cheers! </p>
<p>Thank you for posting this, this is a great project and you have clearly laid out all the steps. </p><p>In your comments on your latest version, it states that 32 is the fastest sampling rate, even though you left in the code for a prescaler of 16. </p><p>Have you been able to get any faster rates with lower prescalers?</p>
<p>Hi,</p><p>The 32 prescalar works well with the latest high speed sd card, producing a sample rate of 38.3Khz on my arduino mega.</p><p>A prescalar of 16 should interrupt at 72Khz- this rate will not leave enough time for the buffer writes to complete. 512 bytes would be read in 512*1000/72000 mS:</p><p>7.1ms</p><p>This will almost certainly be less than the write time for the buffer at the current speed of software and sdcard.</p><p>So the interrupt will prevent the completion of the write- hence the degradation in observed frequency response. (As you observed)</p><p>I find the audio quality for 38.3Khz very acceptable-</p><p>Have you got it working?</p><p>.</p><p>Reducing the buffer size is not an option- according to best advice 512 bytes is the optimal block size.</p><p>A future development might be stereo- two preamps and a 512byte buffer comprising 256 left channel, right channel data pairs......</p><p>My follow up instructable detals how to play back the wav files from the sdcard- I have had no problem with files recorded at 44Khz. So 38.3Khz files will work quite happily.</p>
<p>The 16 prescaler actually slows down the sample rate compared to the 32 prescaler. With the 32 prescaler, i was showing ~38kHz sample rate. With the 16 prescaler, I was showing ~31.8kHz sample rate. Is this because of the interrupts?</p>
<p>thanks for posting this! a lot of great info here. Do you think it's possible to increase the sampling rate above 9.4kHz? is there a limit to how fast you can interface with the sd card?</p>
<p>I did a re-write this evening, utilizing double buffering.</p><p>I now have the frequency easily up to 19Khz, with very good clarity.</p><p>Further testing at higher frequencies is needed, but results are very promising.</p>
<p>I investigated sampling with pre-scalars of 16 (72Khz), 32,(36Khz), 64(18.6Khz) and 128 (9.4Khz). I have left the necessary code in the startad subroutine for those who wish to try.</p><p>Without doubt, the limiting factor is the 512 byte block transfer to the sd card.</p><p>I believe that it is unlikely that further optimization is possible within the block write section of the code.</p><p>Obviously, if it takes longer than the sample period to write a sd block, then the audio is missed (or the frequency perverted) and the sound quality collapses.</p><p>At present, 9.4Khz is the limit for my SDcard writer.</p><p>No doubt card writer technology will speed up.</p><p>I have read claims for faster (beta) versions for sd libraries. However I wanted to use the standard sd library.</p><p>Faster sampling is possible if memory is used as the buffer, rather than the sd card.</p><p>My best optimization for that system allowed for a 6000 byte buffer on a Mega.</p><p>At 38 Khz, only a fraction of a second can be read. This is no good for recording audio, but has applications as an oscilloscope / fast logger.</p>
<p>This is simply incredible. I love how you go over wav files. I always thought about this but I never thought it could actually be done with an arduino. Thanks for sharing </p>
<p>Thanks for that.</p><p>Let us know if you you get it up and running.</p>

About This Instructable


108 favorites


More by DavidPatterson: Arduino Mega GPS with LCD and SD Logging Arduino High speed Oscilloscope with PC interface Playing audio sound files ( wav ) with an Arduino
Add instructable to: