[SOLVED] So close to getting four MPR121 boards working... What's wrong?

I’ve thoroughly looked around and a lot of people are trying to do the same thing as I am: make a 48-key capacitive touch MIDI device (I’m using an Arduino Nano 3.0). Saving all the MIDI stuffs for later, I’m just trying to get the touch working. I already cut the presoldered ADDR-GND connection on all four breakouts and reassigned them with jumpers; the full wiring can be seen in the picture. However, I’m pretty sure the problem is in my code.

I could get two of them working just fine on addresses 0x5A and 0x5B, but when I tried connecting four, only 0x5B and 0x5C worked correctly. 0x5A didn’t give any response at all, and 0x5D would act normally until any one of its pins were touched, and then it would say all of them were touched at once and stay on forever even when I release it. Surely I’m just doing some math wrong or some other stupid error, but I can’t seem to find it; perhaps someone here will have better luck. It’s just a modified version of the example that comes with the library:

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

#define numBoards 4 // Number of MPR121s used
int irqpin [] = {2, 3, 4, 5};
int ADD [] = {0x5A, 0x5B, 0x5C, 0x5D}; // Corresponding addresses of each board

boolean touchStates [12 * numBoards]; // To keep track of the previous touch states

void setup(){
  for (int i = 0; i < numBoards; i++) {
    pinMode(irqpin [i], INPUT_PULLUP);
    //digitalWrite(irqpin [i], HIGH); //enable pullup resistor
  }
  
  Serial.begin(9600);
  Wire.begin();
  
  for (int i = 0; i < numBoards; i++) {
    mpr121_setup(ADD [i]);
  }
}

void loop(){
  for (int i = 0; i < numBoards; i++) {
    readTouchInputs(i);
  }
  
  for (int t = 0; t < numBoards * 12; t++) {
    Serial.print (touchStates [t]);
  }
  Serial.println ();
  
}


void readTouchInputs(int boardNum){
  if(!checkInterrupt(boardNum)){
    
    //read the touch state from the MPR121
    Wire.requestFrom(ADD [boardNum],2); 
    
    byte LSB = Wire.read();
    byte MSB = Wire.read();
    
    uint16_t touched = ((MSB << 8) | LSB); //16bits that make up the touch states

    
    for (int i=0; i < 12; i++){  // Check what electrodes were pressed
      if (touched & (1<<i)) {
        if (touchStates [i + boardNum * 12] == 0) {
          //pin i was just touched
//          Serial.print ("pin ");
//          Serial.print (i + boardNum * 12);
//          Serial.println (" was just touched");
        
        } else if (touchStates[i + boardNum * 12] == 1) {
          //pin i is still being touched
        }  
      
        touchStates [i + boardNum * 12] = 1;      
      } else {
        if (touchStates [i + boardNum * 12] == 1) {
//          Serial.print ("pin ");
//          Serial.print (i + boardNum * 12);
//          Serial.println (" is no longer being touched");
          
          //pin i is no longer being touched
        }
        touchStates [i + boardNum * 12] = 0;
      }
    
    }
    
  }
}




void mpr121_setup(int address){

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

  // Section B - Controls filtering when data is < baseline.
  set_register(address, MHD_F, 0x01);
  set_register(address, NHD_F, 0x01);
  set_register(address, NCL_F, 0xFF);
  set_register(address, FDL_F, 0x02);
  
  // Section C - Sets touch and release thresholds for each electrode
  set_register(address, ELE0_T, TOU_THRESH);
  set_register(address, ELE0_R, REL_THRESH);
 
  set_register(address, ELE1_T, TOU_THRESH);
  set_register(address, ELE1_R, REL_THRESH);
  
  set_register(address, ELE2_T, TOU_THRESH);
  set_register(address, ELE2_R, REL_THRESH);
  
  set_register(address, ELE3_T, TOU_THRESH);
  set_register(address, ELE3_R, REL_THRESH);
  
  set_register(address, ELE4_T, TOU_THRESH);
  set_register(address, ELE4_R, REL_THRESH);
  
  set_register(address, ELE5_T, TOU_THRESH);
  set_register(address, ELE5_R, REL_THRESH);
  
  set_register(address, ELE6_T, TOU_THRESH);
  set_register(address, ELE6_R, REL_THRESH);
  
  set_register(address, ELE7_T, TOU_THRESH);
  set_register(address, ELE7_R, REL_THRESH);
  
  set_register(address, ELE8_T, TOU_THRESH);
  set_register(address, ELE8_R, REL_THRESH);
  
  set_register(address, ELE9_T, TOU_THRESH);
  set_register(address, ELE9_R, REL_THRESH);
  
  set_register(address, ELE10_T, TOU_THRESH);
  set_register(address, ELE10_R, REL_THRESH);
  
  set_register(address, ELE11_T, TOU_THRESH);
  set_register(address, ELE11_R, REL_THRESH);
  
  // Section D
  // Set the Filter Configuration
  // Set ESI2
  set_register(address, FIL_CFG, 0x04);
  
  // Section E
  // Electrode Configuration
  // Set ELE_CFG to 0x00 to return to standby mode
  set_register(address, ELE_CFG, 0x0C);  // Enables all 12 Electrodes
  
  
  // 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
  
  set_register(address, ELE_CFG, 0x0C);
  
}


boolean checkInterrupt(int boardNum){
  return digitalRead(irqpin [boardNum]);
}


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

Update: Still not quite working... I upgraded to an Arduino Uno because that seems to be more reliable with most projects, as well as moving my setup to a better breadboard to ensure that all connections are solid. Got the same result. I can do 1 - 3 boards just fine, but the 4th one causes the weirdness to happen.

Even stranger, with 4 connected, I tried swapping the boards around. This affects which one(s) do the all-at-once-and-no-release thing, but 0x5A never gives any response no matter which board I use. All 4 work fine individually.

Try installing 4k7 pull up resistors on the bus lines (SDA, SCL). The more nodes you have on the bus the higher the overall capacitance is. The internal pullups are very weak so external pullups may solve your problem.

I had some 10k resistors lying around so I put those on SDA and SCL lines; now none of them give a response. Even with 330 which I also had lying around, nothing.

I had some 10k resistors lying around so I put those on SDA and SCL lines; now none of them give a response. Even with 330 which I also had lying around, nothing.

You cannot use any resistor value! You may damage the Arduino and especially the MPR121s with a value low as 330Ω, that's way to much current to sink (I think the standard says 3mA max).

If you have more than two 10k resistors, use two of them in parallel for each of SDA and SCL (that give you 5kΩ which is almost what I suggested). I hope your chips survived the attack with the 330Ω pull-ups and still answer.

They did survive, but I'm a bit confused as to how this works. Should I be configuring A4 (SDA) and A5 (SCL) as INPUT_PULLUP in addition to the IRQ digital pins? And how come I can use a 5kΩ resistor or no resistor just fine but not 330Ω?

EDIT: Also, I tried it with the two 10kΩs in parallel and still got no result. Still getting the same behavior with no resistor after trying that, so I haven't fried anything (yet)

EDIT: Also, I tried it with the two 10Ωs in parallel and still got no result. Still getting the same behavior with no resistor after trying that, so I haven't fried anything (yet)

10Ω and 10kΩ are apart in a factor of thousand!

I sincerely hope you don't used two 10Ω resistors in parallel or your I2C pins are probably gone (that's almost a short circuit).

And how come I can use a 5kΩ resistor or no resistor just fine but not 330Ω?

The bus members of an I2C bus doesn't actively drive a line to HIGH they just pull them to GND if they want to signal and let the pull-up resistors pull it HIGH. This way you can theoretically mix 3V3 and 5V chips on the same bus by simply install the pull-ups to 3V3 instead of 5V. If you use not external resistor, the Wire library activates the internal pullups (it does this anyway even with external pullups, but it's a very weak pullup in the 20k-30k area) to have a HIGH signal level by default. Do you know the Ohm rule? U = R * I? If you have U fixed at 5V and you vary the resistor you get different currents. By using a smaller resistor values you get a higher current. And if an I2C chips is conforming to the standard it must be able to sink up to 3mA but the 15mA you get with your 330Ω resistors are 5 times more than that.

I sincerely hope you don't used two 10Ω resistors in parallel or your I2C pins are probably gone

10kΩ**** Sorry! Yeah no I most certainly did not use 10Ω resistors, I do know Ohm's law lol anyway, I'm beginning to doubt this is the problem because of the consistency I'm seeing. If I use an external pullup, I get no result, and if I use the internal pullup, it does the same weird behavior exactly how I described every time. I would think if it's some problem with inconsistent communication, I'd get inconsistent results. Thoughts?

I found another potential problem although it may not explain all symptoms you describe.

You check the IRQ lines once in a loop() run. It's not clear from the MPR121 datasheet how long the IRQ is held LOW until it's released. But if the LOW state is quite short (usually enough to trigger an interrupt) you might miss that while reading values from another chip. I would change the sketch to use pin change interrupts and save the reception of an interrupt in a boolean variable until you've time to process it. So in the interrupt handler you only set the boolean variable and handle the issue in the main loop.

Hmm... I tried making it so it checks the interrupt pins all at once at the beginning of the loop and stores them to an array (still only checking once per loop) and now it only works with one board. When I have 2, the second always has all electrodes touched and the first does a weird thing where it won't respond unless they're both touched and then it displays touches from the SECOND board... But hey, it adds more consistency to the symptoms from before so I think you might be onto something.

How would you suggest I add an interrupt to check multiple times per loop? Really the only thing I've done with interrupts is using the Timer One library to make a timed interrupt; is there another way I should do it in this case? I know there are "interrupt pins", but on the Uno and Nano there are only 2 of them whereas I would need 4. Is there a way to do it without getting a Mega or some other board with more interrupt pins?

Search Google for Pin Change Interrupt. Or use one of the 3 libraries the Library Manager in the IDE offers you for that area (search for PCI there). On a ATmega328 base board you can have an interrupt raised for almost any pin of the chip. The two interrupt pins just have a separate interrupt handler for that pin, while the pin change interrupt is one handler for all pins that are configured to fire it. The libraries abstract that problem so you can use the pin change interrupt almost the same as a real interrupt pin.

"On a ATmega328 base board you can have an interrupt raised for almost any pin of the chip." 328/328P has PCINT on all 20 IO pins. You can also have PCINT on the Reset pin and the two crystal pins if they are not being used as Reset and crystal pins (needs fuse changes, otherwise no PCINT 6, 7, and 14), so you could have 23 total. 0 to 23, and there is no PCINT15. INT0 and INT1, the hardware interrupts, even have PCINTs.

The ISR for a PCINT works by reading the register for the Port the interrupt occurred on (B,C,D on a '328) and then determining which bit of the port the interrupt occurred on. Nick Gammon has a page on them here, with response times http://www.gammon.com.au/interrupts

Aaallllright. So, I installed EnableInterrupt.h and modified my code to use that with each of the interrupt pins, and it didn't really make a difference. I'll probably use it in the final version anyway in case I have more intense code in the loop (which I probably will), but for now I think it's a different problem. I did some VERY extensive and tedious testing experiments with swapping out boards and addresses and everything, and I think I've identified the problem: Two of my four boards simply aren't behaving as they should.

I labeled all the boards from 1-4, and basically tried every combination of their order on the breadboard. I've found that boards 1 and 3 work perfectly, but the address pin of 2 and 4 seems to still be "partially" tied to ground (0x5A) somehow, even though a conductivity test with a multimeter says it's not. What's happening is, when I have THREE boards at a time, it works with 2 or 4 on address 0x5A and 1 and 3 on anything else. If 2 or 4 are present on any other address, those boards will do the weird touch-all-no-release thing and the one I actually assigned to address 0x5A gives no response. HOWEVER, another symptom I hadn't noticed before that ties it all together is that if the electrodes of the misbehaving board are pressed, the first one works fine, because the misbehaving one is giving a signal for the same address.

It all makes perfect sense now, except for how to fix it! I already tried just cutting deeper into where the trace was in case I missed anything; it's now visibly cut and still gives the same result. I checked conductivity and no pins are connected to anything they shouldn't be... Do you think the boards are just partially defective/damaged, or is there something else I can do?

EDIT: It occurs to me that the address pin on 2 and 4 could just not be working at all, which, if it defaults to 0x5A (and I believe it does), would explain the weird behavior on that address despite no connection to ground. If this is the case my end question of what to do still stands, but I thought I'd throw that out there as a possibility to consider.

If anyone’s interested, the boards were cheap enough that I went ahead and ordered some more. I tested them out and… confirmed my theory of boards 2 and 4 not being addressable for some reason. Still don’t know why, but they work on 0x5A and the new boards work on any address, so I now have four boards working together! YAY!!!

Thanks for all the insight and new information, this has been a learning experience even if it was ultimately just a defective part.