BBC micro:bit
MPR121 Capacitive Touch

Introduction

The touch pins on the micro:bit are fine for a little bit of playing around. You only have three of them, though, and you have to ground yourself too. If you want to do some more complex circuits with touch input, you will need to use a dedicated integrated circuit. The MPR121 can handle 12 touch inputs using capacitive sensing. This gives you much more scope for making all sorts of custom input devices and sensors, using anything that is reasonably conductive.

I've used the Adafruit breakout board for the MPR121, which cost around £7. Unlike the CAP11xx family of integrated circuits, this chip doesn't drive LEDs for you. You do get 12 inputs on the one breakout though. It works nicely at 3.3V and you communicate with the IC using the i2c protocol.

Circuit

At the bottom of the diagram are the standard i2c connections that you make to the micro:bit. The 12 wires at the top of the diagram should be connected to your conductive material. When you are testing, use a few short jumper leads or some solid core wire.

micro:bit Circuit

Programming

This is enough to get started. It's based on the Adafruit library. I used their default settings for reset and things worked first time. You need to see serial output in the REPL window or a terminal emulator to get this going. If you don't have that, replace it with some output on the LED matrix.

from microbit import *

class mpr121:
    
    def __init__(self):
        self.ADDRESS = 0x5a
        self.TOUCHTH_0 = 0x41
        self.RELEASETH_0 = 0x42
        self.SOFTRESET = 0x80
        self.ECR = 0x5E
        self.MHDR = 0x2B
        self.NHDR = 0x2C
        self.NCLR = 0x2D
        self.FDLR = 0x2E
        self.MHDF = 0x2F
        self.NHDF = 0x30
        self.NCLF = 0x31
        self.FDLF = 0x32
        self.NHDT = 0x33
        self.NCLT = 0x34
        self.FDLT = 0x35
        self.DEBOUNCE = 0x5B
        self.CONFIG1 = 0x5C
        self.CONFIG2 = 0x5D

        
    def write_reg(self,reg,value):
        i2c.write(self.ADDRESS, bytes([reg,value]), repeat=False)
    
    def touched(self):
        self.write_reg(0,0)
        lower = i2c.read(self.ADDRESS,2,repeat=False)        
        return lower[0] + (lower[1]<<8)
        
        
    # reset of the device    
    def reset(self):
        self.write_reg(self.SOFTRESET, 0x63)
        sleep(1)
        self.write_reg(self.ECR, 0x00)
        self.set_thresholds(12, 6)
        self.write_reg(self.MHDR, 0x01)
        self.write_reg(self.NHDR, 0x01)
        self.write_reg(self.NCLR, 0x0E)
        self.write_reg(self.FDLR, 0x00)
        self.write_reg(self.MHDF, 0x01)
        self.write_reg(self.NHDF, 0x05)
        self.write_reg(self.NCLF, 0x01)
        self.write_reg(self.FDLF, 0x00)
        self.write_reg(self.NHDT, 0x00)
        self.write_reg(self.NCLT, 0x00)
        self.write_reg(self.FDLT, 0x00)
        self.write_reg(self.DEBOUNCE, 0x00)
        self.write_reg(self.CONFIG1, 0x10)
        self.write_reg(self.CONFIG2, 0x20)
        self.write_reg(self.ECR, 0x8f)
        

    def set_thresholds(self, touch, release):
        for i in range(12):
            self.write_reg(self.TOUCHTH_0 + 2*i, touch)
            self.write_reg(self.RELEASETH_0 + 2*i, release)



cap = mpr121()
cap.reset()
while True:
    a = cap.touched()
    print('{:012b}'.format(a))
    sleep(20)

Next, I hooked up a buzzer on pin 2 and made a quick Play-Doh keyboard.

micro:bit Circuit

I adapted the program to start and stop the playing of music notes.

from microbit import *
import music

class mpr121:
    
    def __init__(self):
        self.ADDRESS = 0x5a
        self.TOUCHTH_0 = 0x41
        self.RELEASETH_0 = 0x42
        self.SOFTRESET = 0x80
        self.ECR = 0x5E
        self.MHDR = 0x2B
        self.NHDR = 0x2C
        self.NCLR = 0x2D
        self.FDLR = 0x2E
        self.MHDF = 0x2F
        self.NHDF = 0x30
        self.NCLF = 0x31
        self.FDLF = 0x32
        self.NHDT = 0x33
        self.NCLT = 0x34
        self.FDLT = 0x35
        self.DEBOUNCE = 0x5B
        self.CONFIG1 = 0x5C
        self.CONFIG2 = 0x5D
        
    def write_reg(self,reg,value):
        i2c.write(self.ADDRESS, bytes([reg,value]), repeat=False)
    
    def touched(self):
        self.write_reg(0,0)
        lower = i2c.read(self.ADDRESS,2,repeat=False)        
        return lower[0] + (lower[1]<<8)
        
    def istouched(self, electrode):
        pattern = self.touched()
        return (pattern & (1 << electrode)) > 0
        
    def reset(self):
        self.write_reg(self.SOFTRESET, 0x63)
        sleep(1)
        self.write_reg(self.ECR, 0x00)
        self.set_thresholds(12, 6)
        self.write_reg(self.MHDR, 0x01)
        self.write_reg(self.NHDR, 0x01)
        self.write_reg(self.NCLR, 0x0E)
        self.write_reg(self.FDLR, 0x00)
        self.write_reg(self.MHDF, 0x01)
        self.write_reg(self.NHDF, 0x05)
        self.write_reg(self.NCLF, 0x01)
        self.write_reg(self.FDLF, 0x00)
        self.write_reg(self.NHDT, 0x00)
        self.write_reg(self.NCLT, 0x00)
        self.write_reg(self.FDLT, 0x00)
        self.write_reg(self.DEBOUNCE, 0x00)
        self.write_reg(self.CONFIG1, 0x10)
        self.write_reg(self.CONFIG2, 0x20)
        self.write_reg(self.ECR, 0x8f)
        
    def set_thresholds(self, touch, release):
        for i in range(12):
            self.write_reg(self.TOUCHTH_0 + 2*i, touch)
            self.write_reg(self.RELEASETH_0 + 2*i, release)


    
def bit_length(n):
    bits = 0
    while n >> bits: bits += 1
    return bits

def nleds(value):
    img = Image('00000:'*5)
    sp =  img.set_pixel
    counter = 0
    for row in range(0,5):
        for col in range(0,5):
            if counter<value:
                sp(col,row,9)
            else:
                sp(col,row,0)
            counter += 1
    return img    
    
notes = [262,294,330,349,392,440,494,523,587,659,698,784]
cap = mpr121()
cap.reset()
last = -2
while True:
    x = cap.touched()
    # play note from leftmost press only
    leftmost = bit_length(x&-x) - 1
    if leftmost != last:
        if leftmost==-1:
            music.stop(pin2)
            display.clear()
        else:
            music.pitch(notes[leftmost],-1,pin2, False)
            display.show(nleds(leftmost+1))
    last = leftmost    

The use of the if statements in the loop reduces the flicker and clicking you would get by constantly updating the sound and display.

micro:bit Circuit

Challenges

  1. Obviously, implement touch input wherever you previously had a less interesting method.
  2. There is much fun to be had in trying out different things as inputs. Fruit is quite standard these days, so try to be more inventive.
  3. You don't have to use weird things as inputs. Making a decent keypad out of tin foil is quite a mission.
  4. Make some artwork that has touch sensors dotted around it. These could output speech or sound input or activate some other outputs. You could make a robot friend. This breakout leaves most of your micro:bit free to use for impressive effects.
  5. The music example used a simple selection of note frequencies. You could choose your notes more carefully and make more interesting music as a result.
  6. In the example with the buzzer, the code filters out all but the leftmost button being pressed. You could use a different approach. Notes could be produced with combinations of button presses, as you would on a woodwind instrument. A separate button could be used to start and stop the note.
  7. The music example could be adapted for MIDI. That gives you access to more interesting effects and sounds. A nice drum kit or sound effect trigger would be good.
  8. Make two electrical contacts out of two strips of kitchen foil or copper tape and lay them flat and quite close to each other. Write a program that stores the order in which the sensors are touched. Adapt the program to detect when a finger is swiped left or right across your contacts. Build up your program to detect swipes on the vertical axis too.
  9. You can change the sensitivity of the sensor so that proximity triggers a reading. By positioning electrodes carefully, you can make sensors that read position in 2 or 3 dimensions. Some research might be worthwhile before embarking on this.