I wanted to have a two-channel function generator that goes above audio frequencies.
I was inspired by this xmega-based waveform generator Instructable. But instead of using an xmega development board, I used the handy little Brainlink. That's a device with an atxmega 16a4, a Bluetooth adapter, a USB-rechargeable battery, various sensors, and a firmware designed for easy Bluetooth control, all in a cute package. They are sadly discontinued, but can bought for $39 at SurplusShed which is the cost of the parts (and SurplusShed has occasional sales that go up to 50% off all stock).
To get a two-channel function generator that I could control over Bluetooth from my phone, all I had to do was to extend the Brainlink's firmware to add a waveform generator function, and then to write a simple Android app that communicates over Bluetooth with the Brainlink.
Now that this is done, all you need to do is to install the firmware with my Android-based firmware uploader which sends the new firmware (which is backwards compatible) via Bluetooth to the Brainlink, and then use my Android app that controls the function generator. All source code is here.
The Brainlink, of course, remains fully usable for other purposes (e.g., this or this) when you're not using it as a signal generator.
Gratitude: While I have no affiliation with Birdbrain Technologies, the maker of the Brainlink, Tom Lauwers of Birdbrain was pleased to see my earlier firmware developments and very kindly gifted me three more Brainlinks.
Step 1: Upgrade Brainlink firmware
The updated firmware for the Brainlink with waveform generator is backwards compatible, but also adds some new features (e.g., Roomba bridge support) and fixes a nasty buffer overflow bug.
The easiest way to update the firmware over Bluetooth is from an Android phone or tablet. Just:
Before the last step, you might also want to increase the Bluetooth connectivity setting in the Uploader. This sets the Brainlink's Bluetooth module to higher connectivity, making it easier to connect to it in the future (you do lose some battery life).
Step 2: Use waveform generator via Android device
The sine/square/triangle wave function requires an amplitude between 0 and 3.3v (with 254 steps in between). See step 5 for a warning. The square wave generator requires choosing duty cycle (percentage between 0 and 100). The arbitrary wave generator function requires a sequence of voltages, which whole sequence is played at the indicated frequency.
Limitations: The xmega is limited to 1.0 MS/s in single-channel mode and 0.67 MS/s in dual-channel mode. This means that the theoretical maximum for a single-channel square wave is 500 kHz. In practice, about 320 kHz is the highest I'd go for a single-channel square wave, and "square" is a bit of an overstatement (see picture). For sine waves, rather lower frequency is better. Even at 100 kHz, a sine wave doesn't look great, given how few samples it has per period. My experiments suggest that generally dual-channel operation is best at 100 kHz and below.
Step 3: Optional: Bipolar waveform generator
The xmega in the Brainlink has a unipolar Digital to Analog Converter (DAC). This means that it generates a voltage between 0 and 3.3v. If you want to generate a bipolar wave, say between -0.5v and +0.5v (e.g., for audio use), you need to pass the output through a capacitor. A 0.1 microfarad one worked perfectly for me. Note that if you use the capacitor, then the voltage you specify in the app will become the peak-to-peak voltage. Thus, if in the app you specify a symmetric wave of 1v amplitude, you'll get a bipolar wave ranging from -0.5v to +0.5v.
Step 4: Optional: Control from other devices
The custom Brainlink firmware adds three new functions. To access them, first enter Brainlink command mode by sending an ASCII '*' (outside of command mode, the Brainlink emits a repeating 'BL' sequence).
To play a sine, triangle or square wave, use the 'w' function. Just send a w followed by (with less than a second in between characters) seven bytes:
To play an arbitrary wave, use the 'W' function. Just send a W followed by (with less than a second in between characters) 5+n bytes:
To stop playback, send ASCII '@' followed by the channel (ASCII '0' or '1').
Note that the first wave channel ('0') cannot be used simultaneously with the Brainlink buzzer (buzzer will turn the channel off, and vice versa) and the second wave channel ('1') cannot be used simultaneously with the Brainlink IR emitter (IR turns off the wave channel and vice versa).