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.
- Tether for Brainlink's 8-pin port (included with Brainlink, but I recommend buying extra 8-pin 1.25mm pin spacing JST cables on ebay)
- Android device (theoretically any Bluetooth-enabled device will work with your own software, and you can just control the Brainlink directly with Realterm)
- Optional: Capacitor(s) for bipolar mode (I used 0.1 microFarad)
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:
- make sure your Brainlink is fully charged
- download my firmware update app from Google Play
- pair your Android device with Brainlink (PIN 1234)
- turn off the Brainlink
- connect pins 2 and 8 (numbered from right to left; see photo) on the Brainlink's 8-pin port
- turn on Brainlink while keeping pins connected (blue firmware update light should turn on)
- run Brainlink Firmware Uploader on your Android device
- choose your Brainlink device (typically shows up as RN42-xxxx) and whether you want firmware optimized for connecting to a Roomba 500+ or for one of the older Roomba 400 / Create robots
- tap on "Program device"
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
- Install Brainlink Wave Generator on your Android device.
- If you're using a different Android device from the previous step, pair your Brainlink with it.
- Make sure Brainlink is turned on.
- Plug tether into the 8-pin port on the Brainlink, making sure no wires short together. (If you use this configuration a lot, you might make a modified tether where the lines irrelevant to the function generator are cut off.)
- Use Ohm's Law to ensure that the current draw at the voltages you want to generate won't exceed 20mA, as the Brainlink's pins are rated only for 20mA. For instance, if you're connecting to a 50 ohm load, make sure no voltage exceeds V=IR=(0.020)(50)=1v. If you're connecting to a 1 mega-ohm oscilloscope, no worries.
- Set the settings for channel 1 or channel 2 and press the right Play button.
- The waveform generator output will come from the pins marked in the photo. (On the original 8-pin tether, the CH1 and CH2 pins are blue, and the GND pin is black.)
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:
- byte 1: channel, either ASCII '0' or ASCII '1'
- byte 2: type, either ASCII 's' (sine), 't' (triangle) or 'q' (square)
- byte 3: duty, between 0x00 and 0x3F; only matters for square waves and is otherwise ignored; however, I recommend setting it to 0x20 for other waves in case I want to use it to describe sawtooth waves in the future
- byte 4: amplitude, between 0x00 (=0v) and 0xFF (=3.3v)
- bytes 5-7: frequency in Hz, 24-bit number, most significant byte first
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:
- byte 1: channel, either ASCII '0' or ASCII '1'
- bytes 2-4: frequency in Hz, 24-bit number, most significant byte first
- byte 5: number n of data points (between 1 and 0x40)
- bytes 6 through 6+n-1: amplitudes, between 0x00 (=0v) and 0xFF (=3.3v)
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).