I'm not sure there will be too much interest in this but it belongs here so I'm adding it to our repository. One of my projects is LED under-cabinet lights and in a kitchen with messy hands I don't want to be touching things. My favorite solution is to tape a strip of aluminum foil (could be copper tape) to the very bottom inside lip of the upper cabinets. With things tuned properly, I can activate the sensor by placing the back of my hand or elbow or whatever on the outside of the cabinet. This has the added benefit of making a sensor that is 12"x2" so I don't have to find a precise spot up underneath the cabinet which is a pain so why bother?
In a previous house I had just ripped off this code (http://playground.arduino.cc/Main/CapacitiveSensor?from=Main.CapSense) and sprinkled in a little bit of sleep here and there but basically ignored duty cycle since this is plugged into AC power. Well, I've picked the code back up and tightened it up considerably with an eye towards keeping very high sensitivity while keeping duty cycle as low as possible. I can now take 100 readings in 26ms and then sleep for 250ms and it feels as if the sensor is always active. This is a vastly lower duty cycle than the default code and could be useful in battery scenarios with such a low duty cycle (should be able to squeeze 4 years from a pair of AAs with a low clock_div and no LDO).
Video of Code Running
Schematic and demo video from original page:
(http://playground.arduino.cc/uploads/Main/CapSense.gif)
EDIT: Changed the LED pin to 9 to use the Moteino's LED and to avoid conflicts with the radio. I was using pin 11 since I was developing on an UNO.
// If using a laptop, make sure it is plugged in as being on battery can result in *very* different timings!
//
// ======================================== USER-SET DEFINITIONS ========================================
#define sPin PD7 // 1MOhm resistor between sPin and rPin
#define rPin PD4 // 18pF capacitor between rPin and GND; Sensor connected to rPin
#define TIME_OUT_MAX 350ul // Set calls to 1 and this to 50,000 and observe the natural threshold, then set this to much less than natural
#define CALLS 100 // At very short timeouts the number of calls needs to be increased
#define THRESHOLD 0.92*CALLS*TIME_OUT_MAX // At very short timeouts, the coefficient gets close to 1
#define LOOP_DELAY 250 // in ms
#define ON_INTENSITY 100 // LED brightness using PWM; select values between 0 and 255 - Must be on Pin 9
#define DEBUG 0 // Toggle serial output on/off - Baud speed is 115,200 when on
//
// ================================= FAST PORT MANIPULATION DEFINITIONS =================================
#define sPin_WRITE_HIGH PORTD |= _BV(sPin)
#define sPin_WRITE_LOW PORTD &= ~_BV(sPin)
#define rPin_MODE_INPUT DDRD &= ~_BV(rPin)
#define rPin_WRITE_LOW PORTD &= ~_BV(rPin)
#define rPin_READ PIND & _BV(rPin)
// ======================================================================================================
static inline unsigned long SenseOneCycle(void) {
uint16_t total=0;
rPin_WRITE_LOW; // Measure low-to-high transition
sPin_WRITE_HIGH;
while (!(rPin_READ) && (total<TIME_OUT_MAX)) total++;
if (total>TIME_OUT_MAX) return TIME_OUT_MAX;
rPin_WRITE_LOW; // Measure high-to-low transition
sPin_WRITE_LOW;
while (rPin_READ && total<TIME_OUT_MAX) total++;
return total;
}
int main(void) { // =============== Setup code goes here ===============
rPin_MODE_INPUT;
interrupts();
DDRB |= _BV(PB1); // Allow pin 9 to control the LED's brightness
// Timer 0 initialization from wiring.c for a ATmega 328P (Arduino Uno rev 3) + 12 bytes to sketch size
TCCR0A = _BV(WGM01) | _BV(WGM00); // set timer 0 prescale factor to 64
TCCR0B = _BV(CS01) | _BV(CS00); // set timer 0 prescale factor to 64
// Timer 1 initialization from wiring.c for an ATmega 328P (Arduino Uno rev 3) + 12 bytes to sketch size
TCCR1A = _BV(COM2A1) | _BV(WGM20); // Enable timer 2 to _delay_ms() works properly
TCCR1B = _BV(CS22); // set clkT2S/64 (From prescaler)
#if DEBUG // Initialize the UART if DEBUGging
uint16_t start, elapsed;
TIMSK0 = _BV(TOIE0); // enable timer 0 overflow interrupt
Serial.begin(115200);
#endif
for (;;) { // =================================== Loop Forever ===================================
uint16_t total=0;
#if DEBUG
start = micros();
#endif
total = 0;
for (uint8_t i=0; i<CALLS; i++) total += SenseOneCycle(); // Make successive quick calls and display the total
OCR1A = (total<THRESHOLD) ? ON_INTENSITY : 0; // Set the LED's brightness based on total (using macro expansion)
#if DEBUG
Serial.print(total);
Serial.print("\t\tCycle took ");
elapsed = micros() - start;
Serial.print(elapsed);
Serial.print(" us\n");
#endif
_delay_ms(LOOP_DELAY);
} // =================================== End Loop Forever ===================================
}