From Badge team
Jump to navigation Jump to search

<<Back to documentation


The Freescale/NXP MPR121 serves as both the capacitive touch controller and as a GPIO expander on the badge. It is connected to the ESP32 through I2C and an interrupt line.


Full MPR121 Datasheet

AN3891 MPR121 Baseline System Application Note

AN3894 MPR121 GPIO / LED Driver Application Note


  • The MPR121 is connected to the ESP32 through I2C on pins IO26 (SDA) and IO27 (SCL).
  • Software pullups are not necessary, as there are two pullup resistors on the board.
  • The MPR's interrupt pin is connected to IO25 on the ESP.
  • Its I2C slave address is 0x5A.

The MPR121 has twelve electrode connections (ELE0-11), of which eight can be used as GPIO. We are using the last four electrode connections as I/O.

MPR121 Electrode connections
Electrode GPIO Function / direction Connection
ELE0 - Touch A
ELE1 - Touch B
ELE2 - Touch Start
ELE3 - Touch Select
ELE4 GPIO0 Touch Down
ELE5 GPIO1 Touch Right
ELE6 GPIO2 Touch Up
ELE7 GPIO3 Touch Left
ELE8 GPIO4 Push/pull output Vibration motor
ELE9 GPIO5 Input TP4056 Charge status
ELE10 GPIO6 Push/pull output WS2812 / SD Card power enable
ELE11 GPIO7 Input SD Card detect


The most important function of the MPR121: capacitive touch. I (Kartoffel) will describe how I was able to get it to work, though it might not be ideal and definitely needs tweaking. I left a lot of registers unexplored, and did not implement the over current detection which can halt the IC.

The basic setup steps:

  • Initialize global baseline filter (registers 0x2B to 0x40) - see AN3891 for information about the baseline system.
  • Set the touch and release thresholds for each electrode (registers 0x41 to 0x5A).
  • Set electrode sample interval (register 0x5D) - this directly influences the current consumption.

Finally, to get the MPR121 into run mode:

  • Enable the electrodes for touch detection (register 0x5E) - set this to 0x08 to enable just ELE0-ELE7 to make sure we can use the rest as GPIO.

Now the MPR is in run mode and scanning the touch electrodes.

When the state of an electrode changes the interrupt pin will go low, and the state should be read by the ESP. Register 0x0 holds the touch status of ELE0 to ELE7.


We are using ELE8-11 (GPIO4-7) as GPIO. The MPR uses eight registers to control its GPIO pins:

GPIO Registers
Register Function
0x73 GPIO Control 0
0x74 GPIO Control 1
0x75 GPIO Data
0x76 GPIO Direction
0x77 GPIO Enable
0x78 Data Set
0x79 Data Clear
0x7A Data Toggle

In order to use the GPIO pins, we first have to initialize them:

  • Set the GPIO direction of IO4 and IO6 as output, IO5 and IO7 as input. (adress 0x76, data 0x50)
  • Set the control registers. For CMOS outputs and inputs without pullups, both of these should be set to 0 for GPIO4-7. (adress 0x73, data 0x00 and adress 0x74, data 0x00)
  • Enable GPIO4-7 by writing 0xF0 to the GPIO Enable register. (adress 0x77, data 0xF0)

Next, the two output pins can be set to HIGH, LOW, or their state can be toggled with the Data Set, Data Clear, and Data Toggle registers. The state of the input pins can be read in register 0x01.

The GPIO5 and GPIO7 inputs have external pullup resistors, so they do not need internal bias.


The IRQ-pin is connected to the ESP32 on IO25. It is an active-low pin that triggers on a touch-event (being touched or no longer being touched) and resets upon reading the registers via I2c. That way you can easily do an interrupt in your code or choose to ignore inputs until you have time to handle them.


No hacks necessary :)

<<Back to documentation