capsense help

hey guys

so im building an instrument with capacitive touch sensors for keys.
i have the amazing MPR121 capacitance breakout board and it works wonderfully.

however, it only enables me to get input from 12 electrodes. i need 13 for a full octave (c to c’).

i downloaded capsense, and tried to implement the code into my pre-existing MPR sketch, and well - i do get input from the capsense button on the arduino - but the latency is around 1 second!

if you could help me out, and tell me why my code slows down like that - i would be a very happy person.

here is my messy code - capsense is //commented. MPR121 code is taken from


//#include <CapSense.h>
#include "mpr121.h"
#include <Wire.h>
#include <SimpleMessageSystem.h> 

//CapSense   cs_19_15 = CapSense(2,3);

int irqpin = 12;  // Digital 12
boolean touchStates[12]; //to keep track of the previous touch states

void setup(){
  pinMode(irqpin, INPUT);
  digitalWrite(irqpin, HIGH); //enable pullup resistor


void loop(){ 
//    long tot = cs_19_15.capSense(30);
//  Serial.print(tot);
//  Serial.println("\t");
//  if(tot < 5000){
//    digitalWrite(13, LOW);
//  }
//  if(tot > 5000){
//    digitalWrite(13, HIGH);
//  }
//  delay(10);

  if (messageBuild() > 0) { // Checks to see if the message is complete and erases any previous messages
    switch (messageGetChar()) { // Gets the first word as a character
    case 'r': // Read pins (analog or digital)
      readpins(); // Call the readpins function
      break; // Break from the switch
    case 'w': // Write pin
      writepin(); // Call the writepin function



void readTouchInputs(){
    //read the touch state from the MPR121
    byte LSB = Wire.receive();
    byte MSB = Wire.receive();
    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] == 0){
          //pin i was just touched
          Serial.print("pin ");
          Serial.println(" was just touched");
            digitalWrite(i+2, HIGH); 
            digitalWrite(13, HIGH); 
        }else if(touchStates[i] == 1){
          //pin i is still being touched
        touchStates[i] = 1;      
        if(touchStates[i] == 1){

          Serial.print("pin ");
          Serial.println(" is no longer being touched");
            digitalWrite(i+2, LOW); 
            digitalWrite(13, LOW); 
          //pin i is no longer being touched
        touchStates[i] = 0;

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
  // Set ELE_CFG to 0x00 to return to standby mode
  set_register(0x5A, 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

boolean checkInterrupt(void){
  return digitalRead(irqpin);

void set_register(int address, unsigned char r, unsigned char v){

//commence max communication!

void readpins(){ // Read pins (analog or digital)

  switch (messageGetChar()) { // Gets the next word as a character

    case 'd': // READ digital pins

    messageSendChar('d');  // Echo what is being read
    for (char i=2;i<14;i++) {
      messageSendInt(digitalRead(i)); // Read pins 2 to 13
    messageEnd(); // Terminate the message being sent
    break; // Break from the switch

  case 'a': // READ analog pins

    messageSendChar('a');  // Echo what is being read
    for (char i=0;i<6;i++) {
      messageSendInt(analogRead(i)); // Read pins 0 to 5
    messageEnd(); // Terminate the message being sent



void writepin() { // Write pin

  int pin;
  int state;

  switch (messageGetChar()) { // Gets the next word as a character

    case 'a' : // WRITE an analog pin

    pin = messageGetInt(); // Gets the next word as an integer
    state = messageGetInt(); // Gets the next word as an integer
    pinMode(pin, OUTPUT); //Sets the state of the pin to an output
    analogWrite(pin, state); //Sets the PWM of the pin 
    break;  // Break from the switch

    // WRITE a digital pin
  case 'd' : 

    pin = messageGetInt();  // Gets the next word as an integer
    state = messageGetInt();  // Gets the next word as an integer
    pinMode(pin,OUTPUT);  //Sets the state of the pin to an output
    digitalWrite(pin,state);  //Sets the state of the pin HIGH (1) or LOW (0)



So far I get mixed results when playing with cap sense but I can tell you that how long it takes to sense is a matter of how long it takes to bleed the cap through the resistor, the higher the value of those two the longer it takes to bleed. You have with resistor-capacitor setup what is called the time constant, R x C (resistance times capacitance) which I find a little funny with cap sense since changing C is how it works and yet time 'constant'... oh well, it's probably a geek thing.

Here is the most minimal cap sense I know of. It works but I wouldn't use it as is outside of test.

hey smoke

well, the code i implemented is copy/pasted from the tutorial you posted. when i run the tutorial by itself, the response is fine.

maybe the two libraries are having a conflict?

could anyone possibly point out some errors i've made?


Just saying that between the resistors and capacitive elements you might likely be able to change values of those and the same code be sensing quicker. The smaller the resistor the faster the capacitor will bleed, the smaller the capacitor the faster it will reach low value.

I haven't dug into the example, I've done and played with two playground cap sense sketches and evolved setups from there but as I say, I got mixed results. A couple were good as far as making cheap buttons.

I have to go out now but I'll be back.

any thoughts on how i could fix my code?


dataplex: any thoughts on how i could fix my code?


GoForSmoke is saying that your code is probably fine, but you should change your hardware.

but i’m saying my hardware works fine if capsense is running as a separate sketch.

im trying to find out the conflict between my old patch and the capsense addition…

I don’t see anything there that looks like this or that could be anywhere as fast as this:

char getcap(char pin)
char i = 0;
KEYDDR &= ~pin; // input
KEYPORT |= pin; // pullup on
for(i = 0; i < 16; i++)
if( (KEYPIN & pin) ) break;
KEYPORT &= ~pin; // low level
KEYDDR |= pin; // discharge
return i;

That code runs -fast-. Direct port manipulation fast not copy the registers, do the function, copy the registers back kind of fast but -do it now- kind of fast. The reason being is that’s how fast that version bleeds down.
Other versions of cap sense I have made work run in the near-milliseconds to milliseconds fast but at least don’t put bare wire between a finger and a pin.

What I do see is code that has a lot of sequence per loop (some with serial reads/writes) and maybe an interrupt with an I don’t know how long IRQ (since I’m not going to become an expert in the libraries used any time soon).

Perhaps not reading the breakout board every time in loop or reading the 13th sensor before that would work? The cap sense code I linked to should take microseconds and in your case I might add cli and sei around it just to keep the other code from screwing it up.

this really sounds like a great solution smokey!

what is cli and sei?

i am spending the afternoon trying this out, thanks so much!

cli(); // clear interrupts, turns interrupts off sei(); // set interrupts, turns interrupts on

You use these so that during the loop count an interrupt doesn't throw that short count off.

Note that code goes with the hardware setup in that example. With higher resistor-capacitor values the bleed-down might take a long time in Arduino cycles (sometimes 1,000's to 10's of 1,000's or more) and you -don't- want to shut interrupts down that long nor would short interrupts throw such counts off far enough to matter much. So your hardware setup is critical here.

Here is the setup and code for that, note there's different code for different boards too:

Note that it is sensing a finger touch on a bare wire directly onto a pin. He is using the capacitance of a piece of wire (and person when the touch is made) for capacitor and the pullup resistor on the pin (which may vary IIRC by 20%, pullup resistors are not precision parts) for his R-C unit. --- BEWARE! If the person who touches that is carrying a load of static then that could be "all she wrote" for parts of the Arduino. It's a nice little test of concept but would you put that out for regular use? I hope not!

It is a good base for further experiments though and really woke me up as to how fast cap sense might get. You don't have to have a capacitor to have capacitance though I do suggest with low capacitive a 10K or higher resistor would be a good idea. Time constant for filling/bleeding is R x C, resistance x capacitance. Remember that part of the time you empty, part of the time you fill, part of the time you bleed (to digital low, about 1.1V) so it takes longer than just the bleed.