I suspect the world would be better if that percentage were even greater.
Using the Arduino to send audio via pulse width modulation
I’m still interested in doing light based communication, but I haven’t made a lot of progress. I did build an LTSpice model of the circuit I used yesterday, but other than verifying that it probably would work as built (which it did) I didn’t feel like I had enough brain cells working to optimize the circuit. So, instead, I decided to try to see if I could use an Arduino to send reasonably high quality audio over light using pulse width modulation.
It doesn’t really seem all that hard in principle: the Arduino libraries include an analogWrite() command which can be used to generate a PWM signal on an output pin. But the problem is the frequency of operation is quite low: around 500Hz or so. Since I was interested in sending voice bandwidth signals (say sampled at 8000Hz or so) the PWM “carrier” frequency simply wasn’t high enough.
So, I did a bit of digging. It turns out that you can configure the timers on the ATMEGA328 on board the Arduino pretty easily, and if you dig through the datasheet, scratch your head a bit, and then type carefully, you can come up with the right incantation. Which I did: in fact, it worked the very first time I downloaded it to the board.
I recorded a second or so of audio using Audacity, dumped it as an 8 bit raw audio file, and then converted it to bytes. I then created a very simple program which simply copies each byte to the PWM overflow register, and then delays for 125 microseconds (1/8000 of a second). Other than that, just some simple bit twiddling to change the PWM prescaler to operate at the full 16Mhz clockrate, and… voila.
Witness the video:
Here’s the core of the code (the actual audio data has been stripped for brevity):
[sourcecode lang=”c”]
#include <avr/pgmspace.h>
prog_uchar bwdat[] PROGMEM = {
0x80, 0x80, 0x80, 0x7f, 0x80, 0x80, 0x80, 0x81, 0x80, 0x80, 0x80, 0x80,
// … lots of lines deleted for brevity…
0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x80, 0x7f, 0x7f, 0x80, 0x80, 0x80,
0x80, 0x81, 0x81, 0x80, 0x80, 0x81
} ;
void
setup()
{
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS20) ;
OCR2A = 180;
}
void
loop()
{
int i ;
char ch ;
for (i=0; i<sizeof(bwdat); i++) {
ch = pgm_read_byte_near(bwdat+i) ;
OCR2A = ch ;
delayMicroseconds(125) ;
}
}
[/sourcecode]
Comments
Comment from Panzi
Time 4/10/2012 at 10:52 am
AWESOME!! You are a Genius!!
Comment from Anthony Webb
Time 7/20/2012 at 7:43 am
Hello Mark, very nice work there! I’ve got a question which you may have an answer to given some of the work you have done with audio. I’d like to have my arduino record FM radio using a chip like (http://www.sparkfun.com/products/11083)
If I am reading your post correctly it might not work to do an analogRead() on the audio output pins and write the data to a file on an SD card right? Any ideas how this could be made to work given your experience with arduino/audio?
Comment from Anthony Webb
Time 7/20/2012 at 7:52 am
One other thing, I will note that I also have a beaglebone I have been messing around with. If the arduino simply isnt fast enough to do this perhaps the bone would be? Seems to me like there are simple recording devices out there (doesnt hallmark put them in their greeting cards now days?) that are capable of recording audio, so I dont know why anarduino couldnt?
Comment from Webb Anthony
Time 2/9/2013 at 1:15 pm
Anthony Webb’s forehead looks cooL! Where did you buy that forehead?
Comment from Dylan
Time 5/29/2016 at 12:51 am
Hi Mark,
Excellent post. Five years old and still very useful! I just had a question.
What did you use to convert the audacity raw file from hex to 0xff format to copy and paste into the program? Thank you.
I’m using a mac.
Regards,
Dylan
Comment from Ben
Time 8/6/2011 at 4:53 pm
I’m rather interested in how you made the arduino send the audio via pulse width modulation. Could I get a copy of the whole script?