Adding a capacitive touch module to the door

Capacitive touch sensors

Disclaimer: I am not an expert in electronics or programming. That's the way I did it, and that's not necessarily the correct way to do it.

The modified hinges allow me to run 8 wires to the door. At first I thought I would use all 8 wires to connect two RGB LEDs (for EMRG+ID lights) to the TLC5947 LED breakout (R+G+B+ground each). Since I now want to use RGBW NeoPixels, I only need 3 wires (5V + ground + data line) and have 5 left over.

Adding touch buttons to the door

This gave me the idea to use the leftover wires for a second MPR121 capacitive touch module directly inside the door. This way all the buttons in the door could be real touch buttons and not just fake sticker buttons. I carved depressions in the shell to see if the breakout board would fit in the upper segment.

Two breakout boards sharing one I2C bus

Although I2C (Wikipedia) is a serial communication bus, the breakout boards are actually connected in parallel, not in series. That means both breakout boards are connected to the same SCL (serial clock line) and SDA (serial data line) GPIOs. Below is a wiring example similar to my test setup.

Note: If several breakout boards are connected to one I²C bus, their pull-up resistors are connected in parallel and decrease the total resistance. This can become problematic if you add too many breakout boards. More details here: How many devices can you connect to the I²C bus?

Wiring example
Two capacitive touch sensor boards sharing one I²C bus
Wiring example
Two capacitive touch sensor boards sharing one I²C bus
  • In my current test setup, four wires need to go from the main body through the hinges and into the door: +3.3V, GND, SDA and SCL

The MPR121 breakouts I used support four different I²C addresses.

  • On the first (upper) breakout I connected ADDR with the 3Vo pin for an address of 0x5B.
  • On the second (lower) breakout I connected ADDR with the SDA pin for an address of 0x5C.

Enable I2C interface for touch sensors

Note: Some driver information in this article may be out of date.

Instructions from Adafruit to enable I2C

  • If not already done: first install the helpers:
sudo apt-get install -y python-smbus
sudo apt-get install -y i2c-tools
  • Then connect e.g. via Putty (SSH) and enable the interface. Command to open configuration:
sudo raspi-config
  • Use arrow keys to go to 3 Interfacing Options, confirme with ENTER key
  • Use arrow keys to go to P5 I2C, confirme with ENTER key
  • Question "Would you like the ARM I2C interface to be enabled?": Select YES, confirme with ENTER key
  • Confirme changes once again with OK
  • Back in the settings overview: Use tabulator key to jump to the bottom menu, select FINISH and confirme with ENTER key
  • Question "Would you like to reboot now?": Select YES, confirme with ENTER key
  • Reboot

Since the touch sensors are already connected, you can check whether they are detected after the reboot. To do this, I connected again via Putty/SSH and entered the following command in the console:

sudo i2cdetect -y 1

The two MPR121 modules should be displayed with the defined addresses 0x5B and 0x5C.

.    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- 5b 5c -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --

Using the MPR121 touch sensors in Python

When I added the first MPR121 breakout to my setup, I simply followed the documentation for wiring and installation of the MPR121 library, as well as the example code to use the MPR121 module in Python. To install the MPR121 library (GitHub) just enter this in the console:

sudo pip3 install adafruit-circuitpython-mpr121

If a second breadout board is connected to the I²C bus, you simply need to add the I²C address to the initialization code and add a second initialization for the second breakout. Here is a simplified example:

# Import libraries
import board
import busio
import time
import RPi.GPIO as GPIO
import adafruit_mpr121

# Create I2C bus
i2c = busio.I2C(board.SCL, board.SDA)

# Create two MPR121 objects with specified I2C addresses
mpr121_a = adafruit_mpr121.MPR121(i2c, address=0x5b)
mpr121_b = adafruit_mpr121.MPR121(i2c, address=0x5c)

# Helper variable to prevent input signal spam
touchdelay = False

try:
  while True:
      # get current state of all pins
      touched_a = mpr121_a.touched_pins
      touched_b = mpr121_b.touched_pins
      # only execute when touchdelay is deactivated
      if touchdelay == False: 
          # First touch module
          if touched_a[0]:
              print('A0 touched')
              touchdelay = True
          elif touched_a[1]:
              print('A1 touched')
              touchdelay = True
          elif touched_a[2]:
              print('A2 touched')
              touchdelay = True
          elif touched_a[3]:
              print('A3 touched')
              touchdelay = True
          elif touched_a[4]:
              print('A4 touched')
              touchdelay = True
          elif touched_a[5]:
              print('A5 touched')
              touchdelay = True
          elif touched_a[6]:
              print('A6 touched')
              touchdelay = True
          elif touched_a[7]:
              print('A7 touched')
              touchdelay = True
          elif touched_a[8]:
              print('A8 touched')
              touchdelay = True
          elif touched_a[9]:
              print('A9 touched')
              touchdelay = True
          elif touched_a[10]:
              print('A10 touched')
              touchdelay = True
          elif touched_a[11]:
              print('A11 touched')
              touchdelay = True
          # Second touch module
          if touched_b[0]:
              print('B0 touched')
              touchdelay = True
          elif touched_b[1]:
              print('B1 touched')
              touchdelay = True
          elif touched_b[2]:
              print('B2 touched')
              touchdelay = True
          elif touched_b[3]:
              print('B3 touched')
              touchdelay = True
          elif touched_b[4]:
              print('B4 touched')
              touchdelay = True
          elif touched_b[5]:
              print('B5 touched')
              touchdelay = True
          elif touched_b[6]:
              print('B6 touched')
              touchdelay = True
          elif touched_b[7]:
              print('B7 touched')
              touchdelay = True
          elif touched_b[8]:
              print('B8 touched')
              touchdelay = True
          elif touched_b[9]:
              print('B9 touched')
              touchdelay = True
          elif touched_b[10]:
              print('B10 touched')
              touchdelay = True
          elif touched_b[11]:
              print('B11 touched')
              touchdelay = True
      # Wait 3 seconds to avoid multiple inputs at same time, then reset
      elif touchdelay == True:
          print('PAUSE 0.5 Sec')
          touchdelay = False
          sleep(0.5)
      # Small delay between input scan 
      else:
          sleep(0.25)
  
except KeyboardInterrupt:
  print('Program manually interrupted. GPIO cleanup')

finally:
  GPIO.cleanup()

I added the variable touchdelay to avoid multiple inputs at same time. I also added GPIO.cleanup() at the end in case the program is interrupted. More about GPIO.cleanup() in this article: How to exit GPIO programs cleanly, avoid warnings and protect your Pi (RasPi.tv).