Capacitive Switch: Triggering results in significant reading change

I'm trying to set up a capacitive switch that triggers a relay attached to an overhead light. The capacitive switch is painted onto a wall and is based on this project from bare conductive:

http://www.bareconductive.com/make/make-a-lightswitch-with-electric-paint/

Although I started with the sketch in the tutorial, I eventually ran into problems. The tutorial sketch is based on a specific trigger - if the arduino registers a value above X, it is triggered. As spring moved into summer, the humidity made it very hard to settle on a single, absolute trigger point.

As a result, I tried to move to a floating average trigger point. Instead of triggering at X, the switch triggered at 1.3 Y, where Y is a floating average of previous readings. While this worked in theory, I'm running into trouble in practice.

Essentially what happens is that the floating average stays low (say 150) while the fairly constant reading is high (say around 400). The result is that every 350ms the light turns from off to on, or from on to off. Eventually, the floating average floats up towards the reading and light stops changing.

However, at that point the actual reading plummets, dragging the floating average down with it. Once the floating average gets low enough, the entire cycle starts again.

My suspicion is that this is an electrical problem, but I have no idea what I am missing. Does anyone have any ideas?

thanks!

#include <CapacitiveSensor.h>
#define MAINSpin 13


int lastButtonState;
int currentPinMode = 0;
int running_average = 5;

CapacitiveSensor  cs_4_2 = CapacitiveSensor(4,2);        // Your resistor goes between pins 4 & 2. Your pad of paint should be connected to pin 2


void setup(){
  pinMode(MAINSpin, OUTPUT);
  digitalWrite(MAINSpin, LOW);
  cs_4_2.set_CS_AutocaL_Millis(0xFFFFFFFF);     
  Serial.begin(9600);
}


void loop()
{
  long reading = cs_4_2.capacitiveSensor(100); // defines reading as the reading from the sensor

  Serial.println(reading);
  Serial.println(" ");   // prints reading value to the serial port
  // new serial stuff
  Serial.print("reading = " );
  Serial.print(reading);
  Serial.print("running_average = " );
  Serial.println(running_average);
  
  delay(2);

  // start new stufff

 
  running_average = (1 + (((0.99 * (running_average * 100)) + (0.01 * (reading * 100))) / 100));  // this generates a running average of the reading
                                                                    // but it will take a few cycles to spool up
                                                                    // the +1 is to make sure that the running average never hits zero, which just screws things up
                                                                    // the * 100 is to avoid small numbers that get lost when rounded

  if (reading > (1.3 * running_average) && millis() > 5000) { // triggered if the current reading is significantly higher than 
                                                              // the running average AND the arduino has been on for more than 2 seconds
                                                              // this 2 seconds gives it time to get the average up
    currentPinMode = currentPinMode + 1; // switch to the next state (there are only 2 states - on and off
    if (currentPinMode >= 2) {
      currentPinMode = 0;

    }
    delay(350); // if the conditions were met and the switch happens, this delay gives you a chance to stop touching the switch
  }




  // end new stuff




  switch (currentPinMode) { // this is where the states are stored
  case 0: // off
    digitalWrite(MAINSpin, LOW);
    break;

  case 1: // on
    digitalWrite(MAINSpin, HIGH);
    break;
  }
}

mweinberg:

 running_average = (1 + (((0.99 * (running_average * 100)) + (0.01 * (reading * 100))) / 100));  // this generates a running average of the reading

You're adding 1 to your running average every time around the loop?

I found that the running average was ending up trapped at zero, so I added the +1 to keep it above that. Although now that you ask, that does seem like a bad idea. Maybe I'll pull it out.

But regardless, I was seeing the same sort of behavior before I added it so I don't think that the +1 is directly responsible for the "dropping" problem.

mweinberg:
But regardless, I was seeing the same sort of behavior before I added it so I don't think that the +1 is directly responsible for the "dropping" problem.

I think the problem is that you're adding 1 every time around the loop. After 100 loops you've added 100.

That doesn't explain why the actual reading drops when the switch stops triggering for a few cycles. Regardless of the average, whenever there is parity between the reading and the running average the actual reading drops. Why would adding 1 each time around impact that?

mweinberg:
That doesn't explain why the actual reading drops when the switch stops triggering for a few cycles. Regardless of the average, whenever there is parity between the reading and the running average the actual reading drops. Why would adding 1 each time around impact that?

If you suspect the light/relay affects the reading from the capacitive sensor then write a new program to directly test that theory. Switch it on/off and look at the reading.

int running_average ;
...
 running_average = (1 + (((0.99 * (running_average * 100)) + (0.01 * (reading * 100))) / 100));

No, that's all crazy. You are overflowing intermediate int results, doing most of the work
in floating point, then spoiling it all by truncating back to int.

The overflow accounts for some of the odd behaviour, and the truncation is why you
tried adding +1 (which needs to be + 0.5 in fact).

Do it this way:

float running_average = 0.0 ;
...
  running_average += 0.01 * (reading - running_average) ;

No need for both 0.99 and 0.01 constants, only one floating point multiplication.
If you need an int value as well, add another, int, variable and derive it from
running_average thus:

  int int_average = round (running_average) ;

If you want to do it all in int, you have to used a fixed point representation
since the fractional part of the running average is vital to meaningful results.

MarkT:
Do it this way:

float running_average = 0.0 ;

...
  running_average += 0.01 * (reading - running_average) ;

None of this is necessary, in fact.

The capsense library recalibrates itself over time. The value returned by "CapacitiveSensor::capacitiveSensor(()" is relative to the baseline capacitance.

What fungus said. Plus, capacitive sensors are exquisitely sensitive to interference if not properly shielded. One painted on a wall is going to be highly subject to humidity making the wall slightly conductive.