mpr121 - help reading filtered data

Hi all,
I'm looking for a little guidance communicating with the mpr121 capacitive sensor (i.e. SparkFun Capacitive Touch Sensor Breakout - MPR121 - SEN-09695 - SparkFun Electronics).
I'm trying to read the 10bit filtered data from the sensors vs. the touch data, but seems no matter what I do, I can't get anything but the touch data. I'm hoping it's a matter of having a register set wrong, but I've gone through the docs several times and am just not seeing it.

Here's my sketch:

#include "mpr121.h"
#include <Wire.h>

int ele0_msb = 0;
int ele0_lsb = 0;

unsigned long currenttime = 0;
unsigned long nextsample = 0;
int sampleinterval = 20;
boolean sampleflag = 0;

void setup(){
  Serial.begin(9600);
  Wire.begin();

  mpr121_setup();
}

void loop(){
  currenttime = millis(); 
  if (currenttime >= nextsample) {
    nextsample = currenttime + sampleinterval;
    readData();
  }

}


void readData() {


  Wire.beginTransmission(0x5A);  // begin communication with the MPR121 on I2C address 0x5A
  Wire.send(0x04);  // read ELE0 filtered data LSB
  Wire.send(0x05);  // read ELE0 filtered data MSB
  Wire.requestFrom(0x5A, 2);     // request for the MPR121 to send 2 bytes

  // check to see if we've received the byte over I2C
  if(Wire.available() >= 2)
  {
    ele0_lsb = Wire.receive();
    ele0_msb = Wire.receive();

  }
  Wire.endTransmission();        // ends communication

  sendData();
}

void sendData() {
  Serial.print(ele0_msb, BYTE);
  Serial.print(ele0_lsb, BYTE);
  Serial.print(10, BYTE);
}



void mpr121_setup(void){

  // Section A - Controls filtering when data is > baseline.

  set_register(0x5A, MHD_R, 0x01);
  set_register(0x5A, NHD_R, 0x01);
  set_register(0x5A, NCL_R, 0x00);
  set_register(0x5A, FDL_R, 0x00);

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



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

  set_register(0x5A, ELE1_T, TOU_THRESH);
  set_register(0x5A, ELE1_R, REL_THRESH);

  set_register(0x5A, ELE2_T, TOU_THRESH);
  set_register(0x5A, ELE2_R, REL_THRESH);

  set_register(0x5A, ELE3_T, TOU_THRESH);
  set_register(0x5A, ELE3_R, REL_THRESH);

  set_register(0x5A, ELE4_T, TOU_THRESH);
  set_register(0x5A, ELE4_R, REL_THRESH);

  set_register(0x5A, ELE5_T, TOU_THRESH);
  set_register(0x5A, ELE5_R, REL_THRESH);

  set_register(0x5A, ELE6_T, TOU_THRESH);
  set_register(0x5A, ELE6_R, REL_THRESH);

  set_register(0x5A, ELE7_T, TOU_THRESH);
  set_register(0x5A, ELE7_R, REL_THRESH);

  set_register(0x5A, ELE8_T, TOU_THRESH);
  set_register(0x5A, ELE8_R, REL_THRESH);

  set_register(0x5A, ELE9_T, TOU_THRESH);
  set_register(0x5A, ELE9_R, REL_THRESH);

  set_register(0x5A, ELE10_T, TOU_THRESH);
  set_register(0x5A, ELE10_R, REL_THRESH);

  set_register(0x5A, ELE11_T, TOU_THRESH);
  set_register(0x5A, ELE11_R, REL_THRESH);

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

  // Section E
  // Electrode Configuration (register 0x5E)
  // Set ELE_CFG to 0x00 to return to standby mode
  // Set ELE_CFG to 0x0C to turn on just touch
  // Set ELE_CFG to 0x30 to turn on proximty for all electrodes
  // Set ELE_CFG to 0x3C to turn on BOTH proximity and touch
  set_register(0x5A, ELE_CFG, 0x30); 

  /*
  // Section F
   // Enable Auto Config and auto Reconfig
   set_register(0x5A, ATO_CFG0, 0x0B);
   set_register(0x5A, ATO_CFGU, 0xC9);  // USL = (Vdd-0.7)/vdd*256 = 0xC9 @3.3V   set_register(0x5A, ATO_CFGL, 0x82);  // LSL = 0.65*USL = 0x82 @3.3V
   set_register(0x5A, ATO_CFGT, 0xB5);  // Target = 0.9*USL = 0xB5 @3.3V
   
   */

}


void set_register(int address, unsigned char r, unsigned char v){
  Wire.beginTransmission(address);
  Wire.send(r);
  Wire.send(v);
  Wire.endTransmission();
}

In a nutshell, I'm trying to read from registers 0x04 and 0x05 (LSB and MSB for electrode 0 data), but I seem to only get 0x00 and 0x01.
For those that have worked a bit with this sensor, any ideas?

Thanks in advance for any pointers.

David

    nextsample = currenttime + sampleinterval;

Adding time values is not a good idea. Subtraction is guaranteed to work (as long as the interval is less than 49+ days). Addition is not.

There is a tutorial on the Sparkfun page. Have you looked at that?

Dear davidb,

I'm encountering the same problem, but I think where we can find a solution.
However, trying to find a solution to this problem will just suck you deeper into the programming issue at hand.

It seems the MPR121 simply sends the touch status register data whenever the interrupt pin is high.
However, you want to read data from a different register, so you need to specifically request the data from a different register.

If you look at the datasheet, at section 6.4 'Read and Write Operation Format' you will see that a WRITE operation is fairly straightforward.

[ST][DeviceAddress][W] (AK) [RegisterAddress] (AK) [DATA] (AK) [SP]
where ST = Start Condition, W = Write (=0) and SP = Stop Condition.
straight brackets are master actions and curly brackets are slave operations

  1. You send a Start condition [ST] and the device address with Wire.beginTransmission( 0x5A );
  2. You send the register address you want to write to with Wire.write( 0xXX );
  3. You send the value you want to write with Wire.write( yourValue );
  4. You send a stop condition with Wire.endTransmission();

Now if we take a Byte Read operation, this has a somewhat different format.
[ST][DeviceAddress][W] (AK) [RegisterAddress] (AK) [SR][DeviceAddress][R] (AK)(Data) [NAK][SP]
where ST = Start Condition, SR = Start Condition Repeated, W = Write (=0), R = Read (=1) and SP = Stop Condition.
straight brackets are master actions and curly brackets are slave operations

With this operation, you run into the limitations of the Wire library as it is provided by the Arduino. (Or at least so it seems)

  1. You send a start condition [ST] and the device address with Wire.beginTransmission( 0x5A );
  2. You send the register address with Wire.write( 0xXX );
  3. Uh-oh, we have to send a Start Condition again with the device address (which would be Wire.beginTransmission( 0x5A ); ).

I've looked into the Wire library and the Wire.beginTransmission inherently sends a start condition [ST].
So, this would mean we again send Wire.beginTransmission( 0x5A ), but this time with a READ bit at the end (1 instead of 0).
The function Wire.requestFrom() does this, but internally taps from a different buffer (rxBuffer instead of txBuffer), thus we can't use this.

Another thing we could do is again use Wire.beginTransmission, and bitshift the device address to the left and add the READ bit to the end.
However, the beginTransmission() overwrites the information we had already entered there the first time. This is because the buffer is only send when using endTransmission().

What could be done is that we first send the device address and the register address, then end the transmission (so the buffer is cleared), then restart the transmission with the bitshifted device address (still following?!?). The problem with this is that the endTransmission internally sends a STOP condition.

As you see, there is not a simple solution to this issue. You need to be able to manually send a Start condition [SR] or be able to remove the STOP condition from the endTransmission in order to stick to the format of the MPR121.
Anyone with good suggestions/solutions is welcome to post them! :slight_smile:

Hi
has anyone got this working with Arduino?

I used the 3985 Application note for Serial Communication that comes with the MPR121 to sort this out.

Here's the function below :

int* read_reg(int address, int register_no, int no_of_bytes) {
  int rxbuffer[no_of_bytes];
  Wire.beginTransmission(address);
  Wire.write(register_no);
  Wire.endTransmission(false);
  Wire.requestFrom(address, no_of_bytes);
  for (int i = 0; i < no_of_bytes; ++i) {

    if (Wire.available()) {
      rxbuffer[i] = Wire.read();
      //Serial.println(rxbuffer[i]);
    }
  }
  Wire.endTransmission();
  return rxbuffer;
}

I used this to read raw proximity measurements. It returns a pointer to an array the size of the data requested

Hi,
returning arrays from functions is not supported in c if I'm not mistaken.
Thats why your solution didn't work for me. I slightly changed it to pass the array as parameter (call by reference). Now it's working.
That freaking false in the endTransmission was what I didn't get. Took me hours. Thanks for your solution.

void read_reg(int address, int register_no, int no_of_bytes, int* rxbuffer) {
  Wire.beginTransmission(address);
  Wire.write(register_no);
  Wire.endTransmission(false);
  Wire.requestFrom(address, no_of_bytes);
  for (int i = 0; i < no_of_bytes; ++i) {

    if (Wire.available()) {
      rxbuffer[i] = Wire.read();
      Serial.println(rxbuffer[i]);
    }
  }
  Wire.endTransmission();
}