I’ve been riding on the road now for some years, and while I normally try to avoid it, I do sometimes find myself riding on the road itself rather than on the footpath or bicycle path.
Most of the time, the traffic is fine. I’m mindful of where everyone is, and there aren’t any problems, but I have had a couple of close calls from time to time. Close calls that have me saying “ode for a horn”.
By law we’re required to have a bell on our bikes. No problem there, I have a mechanical one which is there purely for legal purposes. If I get pulled over by police, and they ask, I can point it out and demonstrate it. Requirement met? Tick to that.
It’s of minimal use with pedestrians, and utterly useless in traffic.
Early on with my riding I developed a lighting system which included indicators. Initially this was silent, I figured I’d see the lights flashing, but after a few occasions forgetting to turn indicators off, I fitted a piezo buzzer. This was an idea inspired by the motorcycles ridden by Australia Post contractors, which have a very audible buzzer. Jaycar sell a 85dB buzzer that’s waterproof, overkill in the audio department but fit for purpose. It lets me know I have indicators on and alerts people to my presence.
That is, if they equate the loud beep to a bicycle. Some do not. And of course, it’s still utterly useless on the road.
I figured a louder alert system was in order. Something that I could adjust the volume on, but loud enough to give a pedestrian a good 30 seconds warning. That way they’ve got plenty of time to take evasive action while I also start reducing speed. It’s not that I’m impatient, I’ll happily give way, but I don’t want to surprise people either. Drivers on the other hand, if they do something stupid it’d be nice to let them know you’re there!
My workplace looks after a number of defence bases in South-East Queensland, one of which has a railway crossing for driver training. This particular boom gate assembly copped a whack from a lightning strike, which damaged several items of equipment, including the electronic “bells” on the boom gate itself. These “bells” consisted of a horn speaker with a small potted PCB mounted to the back which contained an amplifier and bell sound generator. Apply +12V and the units would make a very loud dinging noise. That’s in theory; in practise, all that happened was a TO-220 transistor got hot. Either the board or the speaker (or both) was faulty.
It was decided these were a write-off, and after disassembly I soon discovered why: the voice coils in the horn speakers had been burnt out. A little investigation, and I figured I could replace the blown out compression drivers and get the speakers themselves working again, building my own horn.
A concept formed: the horn would have two modes, a “bell” mode with a sound similar to a bicycle bell, and a “horn” mode for use in traffic. I’d build the circuit in parts, the first being the power amplifier then interface to it the sound effect generator.
To make life easier testing, I also decided to add a line-in/microphone-in feature which would serve to debug construction issues in the power amplifier and add a megaphone function. (Who knows, might be handy with WICEN events.)
Replacing the compression drivers
Obviously it’d be ideal to replace it with the correct part, but looking around, I couldn’t see anything that would fit the housing. That, and what I did see, was more expensive than buying a whole new horn speaker.
There was a small aperture in the back about 40mm in diameter. The original drivers were 8ohms, and probably rated at 30W and had a convex diaphragm which matched the concave geometry in the back of the horn assembly.
Looking around, I saw these 2W mylar cone speakers. Not as good as a compression driver, but maybe good enough? It was cheap enough to experiment. I bought two to try it out.
I got them home, tacked some wires onto one of them and plugged it into a radio. On its own, not very loud, but when I held it against the back of a horn assembly, the amplification was quite apparent. Good enough to go further. I did some experiments with how to mount the speakers to the assembly, which required some modifications to be made.
I soon settled on mounting the assembly to an aluminium case with some tapped holes for clamping the speaker in place. There was ample room for a small amplifier which would be housed inside the new case, which would also serve as a means of mounting the whole lot to the bike.
I wasn’t sure what to use for this, I had two options: build an analogue circuit to make the effect, or program a microcontroller. I did some experiments with an ATMega8L, did manage to get some sound out of it using the PWM output, but 8kB of flash just wasn’t enough for decent audio.
A Freetronics LeoStick proved to be the ticket. 32kB flash, USB device support, small form factor, what’s not to like? I ignored the Arduino-compatible aspect and programmed the device directly. Behind the novice-friendly pin names, they’re an ATMega32U4 with a 16MHz crystal. I knocked up a quick prototype that just played a sound repeatedly. It sounded a bit like a crowbar being dropped, but who cares, it was sufficient.
Experimenting with low-pass filters I soon discovered that a buffer-amp would be needed, as any significant load on the filter would render it useless.
A 2W power amplifier
Initially I was thinking along the lines of a LM386, but after reading the datasheet I soon learned that this would not cut it. They are okay for 500mW, but not 2W. I didn’t have any transistors on hand that would do it and still fit in the case, then I stumbled on the TDA 1905. These ICs are actually capable of 5W into 4 ohms if you feed them with a 14V supply. With 9V they produce 2.5W, which is about what I’m after.
I bought a couple then set to work with the breadboard. A little tinkering and I soon had one of the horn speakers working with this new amplifier. Plugged into my laptop, I found the audio output to be quite acceptable, in fact turned up half-way, it was uncomfortable to sit in front of.
I re-built the circuit to try and make use of the muting feature. For whatever reason, I couldn’t get this to work, but the alternate circuit provided a volume control which was useful in itself.
For the line-level audio, there’s no need for anything more fancy than a couple of resistors to act as a passive summation of the left and right channels, however for a microphone and for the LeoStick, I’d need a preamp. I grabbed a LM358 and plugged that into my breadboard alongside the TDA1905.
Before long, I had a working microphone preamp working using one half of the LM358, based on a circuit I found. I experimented with some resistor values and found I got reasonable amplification if I upped some of the resistor values to dial the gain back a little. Otherwise I got feedback.
For the LeoStick, it already puts out 5V TTL, so a unity-gain voltage follower was all that was needed. The second half of the LM358 provided this. A passive summation network consisting of two resistors and DC-blocking capacitor allowed me to combine these outputs for the TDA1905.
One thing I found necessary, the TDA1905 and LM358 misbehave badly unless there’s a decent size capacitor on the 9V rail. I found a 330uF electrolytic helped in addition to the datasheet-prescribed 100nF ceramics.
Since I’m running on batteries with no means of generating power, it’s important that the circuit does not draw power when idle. Ideally, the circuit should power on when either I:
- plug the USB cable in (for firmware update/USB audio)
- toggle the external source switch
- press the bell button
We also need two power rails: a 9V one for the analogue electronics, and a 5V one for the LeoStick. A LM7809 and LM7805 proved to be the easiest way to achieve this.
To allow software control of the power, a IRF9540N MOSFET was connected to the 12V input and supplies the LM7809. The gate pin is connected to a wired-OR bus. The bell button and external source switch connect to this bus with signal diodes that pull down on the gate.
Two BC547s also have collectors wired up to this bus, one driven from the USB +5V supply, and the other from a pin on the LeoStick. Pressing the Bell button would power the entire circuit up, at which point the LeoStick would assert its power on signal (turning on one of the BC547s) then sample the state of the bell button and start playing sound. When it detects the button has been released, it finishes its playback and turns itself off by releasing the power on signal.
Sound effect generator
Earlier I had prototyped a bell generator, however it wasn’t much use as it just repeatedly made a bell noise regardless of the inputs. To add insult to injury, I had lost the source code I used. I had a closer look at the MCU datasheet, deciding to start from a clean slate.
The LeoStick provides its audio on pin D11, which is wired up to Port B Pin 7. Within the chip, two possible timers hook up: Timer 0, which is an 8-bit timer, and Timer 1, which is 16-bits. Both are fed from the 16MHz system clock. The bit depth affects the PWM carrier frequency we can generate, the higher the resolution, the slower the PWM runs. You want the PWM frequency as high as possible, ideally well above 20kHz so that it’s not audible in the audio output, and obviously well above the audio sampling rate.
At 16MHz, a 16-bit timer would barely exceed 240Hz, which is utterly useless for audio. A 10-bit timer fares better, with 15kHz, older people may not hear it but I certainly can hear 15kHz. That leaves us with 8-bits which gets us up to 62kHz. So no point in using Timer 1 if we’re only going to be using 8-bits of it, we might as well use Timer 0.
Some of you familiar with this chip may know of Timer 4, which is a high-speed 10-bit timer fed by a separate 64MHz PLL. It’s possible to do better quality audio from here, either running at 10-bits with a 62kHz carrier, or dropping to 8-bits and ramping the frequency to 250kHz. Obviously it’d have been nice, but I had already wired things up by this stage, so it was too late to choose another pin.
Producing the output voltage is only half the equation though: once started, the PWM pin will just output a steady stream of pulses, which when low-passed, produces a DC offset. In order to play sound, we need to continually update the timer’s Capture Compare register with each new sample at a steady rate.
The most accurate way to do this, is to use another timer. Timer 3 is another 16-bit timer unit, with just one capture compare output available on Port C pin 3. It is an ideal candidate for a timer that has no external influence, so it gets the job of updating the PWM capture compare value with new samples.
Timer 1 is connected to pins that drive two of the three LEDs on the LeoStick, with Timer 4 driving the remaining one, so if I wanted, I could have LEDs fade in and out with it instead of just blinking. However, my needs are basic, and I need something to debounce switches and visibly blink LEDs. So I use that with a nice long period to give me a 10Hz timer.
Here is the source code. I’ll add schematics and other notes to it with time, but the particular bits of interest for those wanting to incorporate PWM-generated sound in their AVR projects are the interrupt routine and the sound control functions.
To permit gapless playback, I define two buffers which I alternate between, so while one is being played back, the other can be filled up with samples. I define these on line 139 with the functions starting at line 190. The interrupt routine that orchestrates the playback is at line 469.
When sound is to be played, the first thing that needs to happen is for the initial buffer to be loaded with samples using the write_audio function. This can either read from a separate buffer in RAM (e.g. from USB) or from program memory. One of the options permits looping of audio. Having loaded some initial data in, we can then call start_audio to set up the two timers and get audio playback rolling. start_audio needs the sample rate to configure the sample rate timer, and can accept any sample rate that is a factor of 16MHz (so 8kHz, 16kHz up to 32kHz).
The audio in this application is statically compiled in, taking the form of an array of uint8_t‘s in PROGMEM.
Creating the sounds
I initially had a look around to see if I could get a suitable sound effect. This proved futile, I was ideally looking around for a simple openly-licensed audio file. Lots of places offered something, but then wanted you to sign up or pay money. Fine, I can understand the need to make a quid, and if I were doing this a lot, I’d pay up, but this is a once-off.
Eventually, I found some recordings which were sort of what I was after, but not quite. So I downloaded these then fired up Audacity to have a closer look.
The bicycle bell
Bicycle bells have a very distinctive sound to them, and are surprisingly complicated. I initially tried to model it as an exponentially decaying sinusoid of different frequencies, but nothing sounded quite right.
The recording I had told me that the fundamental frequency was just over 2kHz. Moreover though, the envelope was amplitude-modulated by a second sinusoid: this one about 15Hz. Soon as I plugged this second term in, things sounded better. This script, was the end result. The resulting bell sounds like this:
So somewhat bell-like. To reduce the space, I use a sample rate of 6.4kHz. I did try a 4kHz sample rate but was momentarily miffed at the result until I realised what was going on: the bell was above the Nyquist frequency at 4kHz, 6.4kHz is the minimum practical rate that reproduces the audio.
I used Audacity to pick a point in the waveform for looping purposes, to make it sound like a bell being repeatedly struck.
I wanted something that sounded a little gutsy. Like an air-horn on a truck. Once again, I hit the web, and found a recording of a train horn. Close enough, but not long enough, and a bit noisy. However opening it up in Audacity and doing a spectrum analysis, I saw there were about 5 tones involved. I plugged these straight into a Python script and decided to generate those directly. Using a raised cosine filter to shape the envelope at the start and end, and I soon had my horn effect. This script generates the horn. The audio sounds like this:
Using other sound files
If you really wanted, you could use your own sound recordings. Just keep in mind the constraints of the ATMega32U4, namely, 32kB of flash has to hold both code and recordings. An ATMega64 would do better. The audio should be mono, 8-bits and unsigned with as lower sample rate as you can get away with. (6.4kHz proved to be sufficient for my needs.)
Your easiest bet would be to either figure out how to read WAV files (in Python: wave module), or alternatively, convert to raw headerless audio files, then code up a script that reads the file one byte at a time. The Python scripts I’ve provided might be a useful starting point for generating the C files.
Alternatively, you can try interfacing a SDCard and embedding a filesystem driver and audio file parser (not sure about WAVE but Sun Audio is easily parsed), this is left as an exercise for the adventurous.
I’ll put schematics and pictures up soonish. I’m yet to try mounting the whole set up, but so far the amplifier is performing fine on the bench.