So I finally have an Arduino. I've been hankering after one for a while now, after seeing what Build Brighton and others have been able to do with them. So I was very happy when my better half paid attention to the hints I'd been dropping and got me an Arduino starter kit for my birthday. That was only a few days ago and thanks to how wonderfully easy the Arduino IDE is, it hasn't taken long for me to get things working. The simplicity of writing the code, hitting upload and seeing the effect on the Arduino within seconds makes it really quick to try out different ideas. The only bit that's tricky (for me at least) is the electronics side of things. Due to a slight fear of accidentally burning out the 8bit AVR chip at the core of the Arduino I'm being super cautious. Though this is due to my at best high school knowledge of electronics. That and the fact that I'm pretty much a software boy.
So what to make first? Well morse code seemed like a good starting point. I'd written an app to output morse on the capslock LED before and the starter kit had LEDs, so it seeded like a good starting point. On closer inspection there was also a piezoelectric buzzer - so even better it was possible to generate a tone too!
Getting the LED to turn on and off was pretty easy and it forms the standard "hello world" program for the Arduino. Getting the buzzer to generate a tone was marginally trickier, but not that hard and basically just consisted of turning it on and off quick enough to cause a vibration and therefore make a sound. With the help of this guide to making the Arduino play the Can-Can I also had details of how to wire the circuit correctly (in particular adding in a suitable resistor to avoid overloading the AVR chip).
Using the starter kits breadboard, jumper wires, a 330 Ohm resistor, an LED and the piezoelectric buzzer I put together this:
The buzzer had it's positive pin wired in to pin 4 on the Arduino and the LED was in turn wired in to pin 13. In the picture above, I arbitrarily used yellow jumper leads for "ground" and green for positive. So one green lead goes to each pin (4 and 13) and only one yellow lead goes to the "ground" on the Arduino.
Initially I hard-coded the message the Arduino was going to output, but having to re-compile each time to change the message seemed a little tedious. So I decided to get the Arduino to read from the serial port, store the characters it read in, then output the relevant morse. To achieve this the program has a 512 byte buffer that it fills up with data read from the serial port. If the buffer is full it signals this by writing back a -1 down the serial port - otherwise it echos back what it has read in. As it plays the morse it moves along the buffer looking for the next character. Once all characters are output it resets the buffer. The program is actually able to read from the serial port whilst it is turning the LED on/off and generating tones. It's only limited by the size of the buffer it is using and the fact that it can read faster from the serial port than it can output morse code.
Most of the work in the code revolved around trying to both read from the serial port and generate tones. If the morse was known ahead of time, it would be pretty easy to right several for-loops to run through each character generating tones until we were finished. However the program would not start reading from the serial port until it had finished outputting the morse. So apart from using some basic state-machine style antics to keep track of which character we were on and whether we were currently during a dot, dash or silent period the major a-ha moment for me was realizing how to easily generate a consistent tones without using delays in the code. In previous code I'd seen for generating tones, the code looked like:
for (long i=0; i < numCycles; i++){ digitalWrite(targetPin,HIGH); // write the buzzer pin high to push out the diaphram delayMicroseconds(delayValue); // wait for the calculated delay value digitalWrite(targetPin,LOW); // write the buzzer pin low to pull back the diaphram delayMicroseconds(delayValue); // wait againf or the calculated delay value }
This is nice and easy to follow, but all those calls to delayMicroseconds mean you are hanging around waiting for the air to move to make a sound.
As I wasn't too concerned with the particular fidelity of the sound from the buzzer I opted for the following instead:
void morse_pulse_on() { digitalWrite(LED_PIN, HIGH); unsigned long period = micros() % buzzer_delay; digitalWrite(BUZZER_PIN, period < (buzzer_delay>>1)? HIGH : LOW); }
This was called every time through the loop() function if a sound needed to be played. As there is no delay in calling it, one could then immediately perform other tasks, such as checking for new serial data or turning the LED on and off. It's quite simple real - the modulus operator (%) is used to get the sub-divide the current time in microseconds into a number between 0 and buzzer_delay (a value pre-calculated to let us generate the right frequency) representing the current "period". If we are halfway through this period then we set the buzzer to HIGH, otherwise we set it to LOW. By repeatedly calling morse_pulse_on we end up with (in this case) a glorious 700Hz tone.
All code for this Arduino morse code program is available on github, as part of my Arduino Sketches project. Hopefully I'll add more code to that project as I get a chance to delve deeper into what the Arduino can do.
Here's a demo of the whole thing in action:
UPDATE: not long after finishing this, I discovered the tone and noTone functions. These produce a better tone than the technique outlined above. Though they do involve use of one of the interrupts on the arduino, that's normally used for PWM on pins 3 and 11 - not that it's a problem for this project.