Pi Synth
Raspberry Pi synth based on the Adafruit CAP1188 8-pad capacitive touch sensor, and other things.
Status
This is first and foremost a playground/hobby-horse, and as such will likely never be complete. As components resolve and stabilize, they'll be split out into separate repos as necessary.
Ergo, at the moment this is probably mostly useful as an example and resource roundup.
License
BSD.
References and Inspiration
- Cap1188 Breakout Documentation adafruit.com
- Configuring i2c on the Raspberry Pi adafruit.com
- Cap Touch on the RasPi adafruit.com
- Raspberry Pi and Arduino Connected Using I2C oscarliang.net
- Adafruit CAP1188 Arduino Library adafruit.com
- How to use Interrupts with Python on the Raspberry Pi and RPI.GPIO RasPI.TV
- Wiring Pi wiringpi.com
- The RPIO 0.10.0 Documentation pythonhosted.org
- FluidSynth elementsofsound.org — sound synthesis
- Mingus code.google.com — the ivories.
BOM
- 1 Raspberry Pi
- 2x AdaFruit Cap1188 8-pad cap touch sensor
- 1x AdaFruit Pi Cobbler
- Solderless Breadboard, jumpers, etc.
Pi Preparation
Software and Configuration
I2C Support
Kernel Modules
Make sure I2C support has been added to the pi, as per #2 above. Specifically:
First, check to see that the i2c components have not been blacklisted
in /etc/modprobe.d/raspi-blacklist.conf
. This will probably be the
case if you're using Raspian.
If it exists, comment out the i2c-bcm2708
entry as such:
# blacklist spi-bcm2708 # OK to leave this blacklisted, not needed for i2c
blacklist i2c-bcm2708
Next, tell the system to load the i2c kernel modules at startup:
$ sudo echo i2c-bcm2708 >> /etc/modules # if it's not already there
$ sudo echo i2c-dev >> /etc/modules # if it's not already there
$ /sbin/reboot
Finally, for convenience, add your account (e.g. pi
) to the i2c
group,
so that you don't have to sudo every command that acceses i2c
:
$ sudo adduser pi i2c
Tools
Make sure the user tools for interacting with i2c
are installed:
$ sudo apt-get install i2c-tools python-smbus
Check
With nothing on the i2c bus, there should be no addresses taken. Note: The "UU" at 3b is due to a kernel audio driver using this address, unrelated to this project.
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
GPIO Support
The forgan.py
script uses RPIO to access the Pi's GPIO pins.
$ sudo pip install rpio
I also recommend installing @drogon's excellent Wiring Pi native GPIO suite. While all of the code here (so far) is written in Python, Wiring Pi includes some great tools including a pin test utility which will likely come in very handy.
Sound and Notes
forgan.py
uses the Mingus Python library
to play individual notes; Mingus in turn uses FluidSynth for sound synthesis. Both packages are required.
$ sudo apt-get install fluidsynth
$ sudo apt-get install python-setuptools # for pip, if you don't already have it
$ sudo pip install mingus
Alternatively, if you prefer easy-install
to pip
, you can probably use that
to install Mingus as well (although I haven't tried it).
Wiring
Perform all wiring and checks while disconnected from the pi.
cap1 (i2c: 0x28
):
Cap1188 Pin | RasPi Pin |
---|---|
VIN | 3v3 |
GND | GND |
SDA | SDA |
SCL | SCK |
RST | - |
AD | 3Vo ON CAP1188. This selects i2c address 0x28. |
INT | 24 |
cap1 (i2c: 0x29
):
Cap1188 Pin | RasPi Pin |
---|---|
VIN | 3v3 |
GND | GND |
SDA | SDA |
SCL | SCK |
RST | |
AD | - |
INT | 24 |
After wiring and double-checks:
- Power off the pi.
- Plug the cobbler onto the pi; when looking down at the pi, with the SD card on the right, the text "FC-26P" on the top of the cobbler's pi-side plug should be right-side-up.
- Power up the pi.
- Once powered up, verify i2c:
pi@pi ~ $ sudo i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- 28 29 -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Log
7/13/2014 Interrupt Stickiness
Symptom
Since moving to interrupt-driven input, there's been a problem where interrupts would not be recognized until some random and unacceptable delay after start from a power-on or reset. However, it reliably worked if I restarted from a previously-running state (after interrupts started being recognized).
Diagnosis
-
Ran the
pintest
tool from the Wiring Pi kit, and verified that all of the GPIO input pins were functioning correctly. -
Tested the RPi's pull-up resistor on the GPIO used for interrupts by measuring potential across the pin and GND when pulled up. 3.3v, check.
-
Removed all non-essential wiring on the IRQ path, leaving only one CAP1188 IRQ pin connected. No change in behavior.
-
Placed the multimeter leads in series between the IRQ pin on the active CAP1188 and the interrupt GPIO. Expected results were that upon startup, the voltage would float around a few millivolts, and then shoot up to 3.3v once an interrupt was detected. Instead, it immediately went to 3.3v on startup.
What was happening is that on startup, the CAP1188's INT flag on register MAIN is set to 1, meaning
that an interrupt is being asserted. This happened whether starting from power-up, or after asserting
the RESET pin. It was unexpected, since according to the datasheet, the default value of the Main
Control Register (address 0x00
) is 0x00
(Datasheet section 5.1).
According to the Datasheet 4.2, driving the RESET pin low after previously driving it high will cause an interrupt to be generated
So, the interrupt was being asserted before I started listening, so I was never seeing a rising edge once I started listening (except, apparently, for noise—eventually, after some period of time I'd see something and clear the interrupt).
Resolution
The resolution was to reset the interrupts by clearing the MAIN_INT flag after driving the RESET pin low.
# set MAIN_INT flag to 0
logging.debug("Resetting interrupts.")
cap1.reset_interrupt();
cap2.reset_interrupt();
After making this change, interrupts are detected immediately after start.
Followup
Going back to the contributed Adafruit example interrupt code for the CAP1188, I noticed the line:
EIFR = 1;
I ignored it, since I hadn't heard of EIFR
(however since I also didn't see it in the Adafruit library or
the contributed code, I should have been suspicous). As it turns out, this is the Arduino External Interrupt
Flag Register. This forum post shed some light: EIFR = 1;
is very important to making the Arduino code work properly; setting MAIN_INT
to low accomplishes basically
the same thing in the case of the Pi.