Electric Imp MP121 Capacitive Sense

I’m trying to use the Sparkfun MPR121 Cap sense breakout board with the imp. I’ve so far got numbers coming in but I haven’t a clue how to interpret that number to figure which electrode has been sensed.

I’m basing my code on this Bildr tutorial: http://bildr.org/2011/05/mpr121_arduino/

So far I have thus code to set it up:

hardware.i2c89.configure(CLOCK_SPEED_400_KHZ); local alsAddr = (0x5A<<1); hardware.i2c89.write(alsAddr, "\\x5E\\x0C"); // make all electrodes enabled imp.wakeup(0.5, readCap);

and then in my readCap function:

`function readCap() {
hardware.i2c89.write(alsAddr, “\x00”);
local result = hardware.i2c89.read(alsAddr, “”, 2);
if (result != null) {

    local bits = (result[1]<<8)|result[0]; // convert to 16 bits
    for (local i=0; i < 12; i++){
        if(bits & (1<<i)){ // no idea what this is doing

imp.wakeup(0.5, readCap);


The number that comes back is anywhere from 3500 up to 4095. If I touch the sensors then it all seems to come back as 4095. I’m guessing that’s a 16 bit number and I have no idea what to do with that!

Completely out of my depth - any help would be appreciated!


The i2c read is returning a bitset: the bottom 12 bits (bits 0-11) indicate the touch status for the 12 inputs.

What this means is that if input 0 is touched, bit 0 will be set (ie, 2^0 = 1). If input 1 is touched, bit 1 will be set (ie, 2^1 = 2). All these numbers added up are the number you’re seeing printed.

The “no idea what this is doing” loop is checking each bit to see if it’s set. If it is, it’s logging that number.

Really, if nothing is touched (and everything is working fine) you should get 0 back. If everything is touched, then you would expect 4095 - this is 1+2+4+8+16+32+64+128+256+512+1024+2048 (ie, all 12 bits are “on”).

Does that help?

Hi Hugo,

that’s great thanks for the explanation. I think I need to set the threshold correctly as I’m getting numbers constantly, but yes 4095 comes back if I touch everything. I’ll see if adding more settings work.

Thanks for your help on this.

FWIW, my i2c code is working now. Using the address with a << 1 works.

The example code on http://devwiki.electricimp.com/doku.php?id=i2c does not give this guidance.

I am trying to get a different I2C device to work. The MMA8452 accelerometer.

I have a question about this line in the code above.
local alsAddr = (0x5A<<1);
Why the “<<1” ?

I am using this code:
local MM8452_ADDR = 0x1D; local whoami = hardware.i2c89.read(MM8452_ADDR, "\\x0D", 1); server.log("Who Am I ==" + whoami);
and getting a null returned.

Looking at the wires on a scope it looks like a Write (expected a Read) is being done to address 0x1C (expected 0x1D).

I added the initialisation code below, and now I always get back 4095 no matter if anything is touching the sensors. At least I’m not getting fluctuating numbers now! I’m just wondering if having the sensors plugged into a breadboard via some headers would signify a touch and therefore skewing everything.

`// MPR121

local MHD_R=0x2B;
local NHD_R=0x2C;
local NCL_R=0x2D;
local FDL_R=0x2E;
local MHD_F=0x2F;
local NHD_F=0x30;
local NCL_F=0x31;
local FDL_F=0x32;
local ELE0_T =0x41;
local ELE0_R =0x42;
local ELE1_T =0x43;
local ELE1_R =0x44;
local ELE2_T =0x45;
local ELE2_R =0x46;
local ELE3_T =0x47;
local ELE3_R =0x48;
local ELE4_T =0x49;
local ELE4_R =0x4A;
local ELE5_T =0x4B;
local ELE5_R =0x4C;
local ELE6_T =0x4D;
local ELE6_R =0x4E;
local ELE7_T =0x4F;
local ELE7_R =0x50;
local ELE8_T =0x51;
local ELE8_R =0x52;
local ELE9_T =0x53;
local ELE9_R =0x54;
local ELE10_T =0x55;
local ELE10_R =0x56;
local ELE11_T =0x57;
local ELE11_R =0x58;
local FIL_CFG =0x5D;
local ELE_CFG =0x5E;
local GPIO_CTRL0 = 0x73;
local GPIO_CTRL1 = 0x74;
local GPIO_DATA = 0x75;
local GPIO_DIR = 0x76;
local GPIO_EN = 0x77;
local GPIO_SET = 0x78;
local GPIO_CLEAR = 0x79;
local GPIO_TOGGLE = 0x7A;
local ATO_CFG0 = 0x7B;
local ATO_CFGU = 0x7D;
local ATO_CFGL = 0x7E;
local ATO_CFGT = 0x7F;
local TOU_THRESH = 0x06;
local REL_THRESH = 0x0A;

local alsAddr = (0x5A<<1);

function write(register, data)
local blah = hardware.i2c89.write(alsAddr, format("%c%c", register, data));

function setupMPR121() {

write(ELE_CFG, 0x00);

// Section A - Controls filtering when data is > baseline.
write(MHD_R, 0x01);
write(NHD_R, 0x01);
write(NCL_R, 0x00);
write(FDL_R, 0x00);

// Section B - Controls filtering when data is < baseline.
write(MHD_F, 0x01);
write(NHD_F, 0x01);
write(NCL_F, 0xFF);
write(FDL_F, 0x02);

// Section C - Sets touch and release thresholds for each electrode
write(ELE0_T, TOU_THRESH);
write(ELE0_R, REL_THRESH);

write(ELE1_T, TOU_THRESH);
write(ELE1_R, REL_THRESH);

write(ELE2_T, TOU_THRESH);
write(ELE2_R, REL_THRESH);

write(ELE3_T, TOU_THRESH);
write(ELE3_R, REL_THRESH);

write(ELE4_T, TOU_THRESH);
write(ELE4_R, REL_THRESH);

write(ELE5_T, TOU_THRESH);
write(ELE5_R, REL_THRESH);

write(ELE6_T, TOU_THRESH);
write(ELE6_R, REL_THRESH);

write(ELE7_T, TOU_THRESH);
write(ELE7_R, REL_THRESH);

write(ELE8_T, TOU_THRESH);
write(ELE8_R, REL_THRESH);

write(ELE9_T, TOU_THRESH);
write(ELE9_R, REL_THRESH);

write(ELE10_T, TOU_THRESH);
write(ELE10_R, REL_THRESH);

write(ELE11_T, TOU_THRESH);
write(ELE11_R, REL_THRESH);

// Section D
// Set the Filter Configuration
// Set ESI2
write(FIL_CFG, 0x04);

// Section E
// Electrode Configuration
// Set ELE_CFG to 0x00 to return to standby mode
write(ELE_CFG, 0x0C); // Enables all 12 Electrodes
write(ELE_CFG, 0x0C);

function readCap() {
hardware.i2c89.write(alsAddr, “\x00”);
local result = hardware.i2c89.read(alsAddr, “”, 2);

if (result != null) {
    local bits = (result[1]<<8)|result[0];
    local key_code = 0;
    for (local i=0; i < 12; i++){
        if (((bits>>i) & 0x01)==1) {

imp.wakeup(0.5, readCap);


OK. I’m an idiot. Having the electrodes plugged into a breadboard via a header did indeed give the impression they were all being touched. Now it all works!

Capacitive touch is most definitely sensitive :slight_smile:

The reason Brendan shifted the address left by one is that the I2C API uses 8-bit addresses. I2C has 7-bits of address plus a Read/!Write bit in the LSB. The reason you saw 0x1C go on the wire is that 0x1D is not a valid Write command as the LSB is not 0.

Some vendor datasheets specify I2C addresses as two separate 8-bit Read/Write address and sometimes they specify just the upper 7-bits. It you have the later variety you need to shift it up.

I’ve updated the i2c API documentation to clarify this. Sorry for your trouble.