In some applications, it is interesting to be able to generate good quality sound using a cheap microcontroller with limited RAM.FLASH and EEPROM memory.
A doorbell that plays a spoken message or a sound file that was previously recorded whenever the button is pressed.
A cat repeller using a PIR sensor or light barrier and that plays a loud barking dog sound whenever motion is detected.
A talking clock, thermometer or multimeter
...
We will use a 5V version of the Arduino Pro Mini, propelled by an ATmega328 running at 16MHz.
Because we want to use 8-bit data sampled at 44,1 kHz to get a reasonable good sound quality, we will need, 44100 * 60 = about 2.6 MBytes for 1 minute of sound.
But the Arduino Pro Mini has limited storage capacity (32 kBytes of Flash memory and 1K Bytes of EEPROM memory), so we need to store the sound data in some kind of external memory.
This can be an external I2C or SPI EEPROM or FLASH memory, but since SD card modules are cheap and easy to find, we choose for this option. The SD card module we use here, has an SPI interface (CS, SCK, MOSI, MISO) and has an on-board 3V3 regulator and a level shifter. The level shifter will take care that the signal levels between the SD card module and the microcontroller are equal. So we can connect the SD card module directly to the Arduino Pro Mini (that uses 5V levels).
Be aware that there are 5V and 3V3 versions of the Arduino Pro Mini. If you want to use a 3V3 Arduino Pro Mini, you need to use an SD card module without a level shifter (so also without a 5V to 3V3 regulator).
Always check the signal levels of the modules that you are using and make sure that their signal levels are compatible. Incompatible signal levels can cause all kind of unexpected problems.
To generate the audio output, we could use an R/2R resistor network connected to 8 digital output pins to convert the 8 bit sound data to an analog signal. We could also use an 8-bit I2C or SPI 8-bit DAC to do the job.
Another alternative, that we will use here, is to convert the 8-bit sound data to a PWM signal, which can be output on one of the PWM output pins of the Arduino. This requires only 1 microcontroller pin.
To reconstruct the original audio signal, all we need is to feed the PWM signal through a low pass filter, so the PWM frequency is suppressed. To get extra suppression of the PWM frequency, we use 2 low pass filters in series to form a 2nd order low pass filter.
Because we are using a 5V Arduino Pro Mini, we need to check that all connections to other modules have compatible signal levels. The micro-SD card module that we are using here, has an on-board 3V3 voltage regulator, since the internal signals are talking to the SD card with 3V3 levels. The micro-SD card module has an SPI bus for communication with the microcontroller. Our microcontroller communicates using 5V signal levels, so we want the SD card module to talk with 5V signal levels. For that purpose, the SD card module has an on-board level shifter, that both converts between the internal 3V3 levels and the 5V levels from and to the outside world.
The level shifter works both ways : external 5V signals coming from the microcontroller are converted to internal 3V3 signals, and internal 3V3 signals are converted to 5V signals going to the microcontroller.
In the PWM to DC filter, I'm using a MCP6022 dual CMOS OPAMP from Microchip. This is a rail to rail output 10MHz bandwidth product OPAMP that can be operated with a single supply up to 5V5.
The picture below shows an overview of the SD card module, with the on-board 3V3 regulator and level shifter.
The picture below shows the micro-SD card module that i used for this project :
The PWM frequency is set in software to 62,1KHz. The duty cycle of the PWM signal is controlled by the audio data. When no audio is present, the PWM duty cycle will be 0%. When the audio data is maximum (8-bit data, so 0xFF), the PWM duty cycle will be 100%.
To filter out the PWM frequency, we need a low pass filter that passes the audio signal, but suppressed the PWM frequency. To get enough suppression, we combined 2 low pass filters R2/C5 and R1/R3/C4 with a OPAMP U4, configured as a buffer, in between.
The buffer takes care that the 2nd low pass filter (R1/R3/C4) does not load the output of the first low pass filter (R2/C5).
We want an audio output that can be connected directly to an AUX input of an amplifier, so we need audio levels as used for a line output, i.e. 1Vrms or 1,4 Vpp (peak to peak). The resistive divider R1 and R3 brings down the 5Vpp audio level to about 1,5Vpp.
That is close enough to the 1,4 Vpp audio level that we wanted. Most amplifier inputs already block DC using a series capacitor, but we also want to be able to connect earphones to the output, so we add a DC blocking filter, C3/R4 so the output swings around 0V.
The following libraries are used in the software :
FatReader made by William Greiman (Copyright 2009)
WaveHC made by Adafruit.(Lady Ada)
The original WaveHC library was written to output 12 bit mono audio using the Adafruit Arduino Wave Shield. On this shield an SPI DAC is used.
Because I wanted to output the audio data using a PWM output on the Arduino, I have adapted the library files.
I added "_local" as a postfix to the filenames of the libraries, and I've put the library files in the same folder as the sketch, so they are not overwritten by library updates.
To generate audio files, you can use Audacity, that is a free audio editing tool.
With this tool you can load an audio file (.wav or .mp3), then convert the stereo tracks to mono, and export the file as a Microsoft .WAV file using unsigned 8 bit PCM.
When you put multiple .WAV files on the SD card, the Arduino wave player will play all the files one by one and stops when all files were completed.
Click here to download the Arduino sketch + libraries for the Arduino wave player
Click here to download audio samples for the Arduino wave player