This is my entry for The Flashing Light Prize 2017. I use the CapSense (CPS) module of a PIC16F1823 to detect when I touch the light bulb. This works only, when the light bulb is off and not connected to plus or minus, so I used two MOSFETs to completely isolate the light bulb from the power rails, and then I can sense the metal case with the PIC.

The timer value is much smaller than with the usual touch pad, maybe because of leakage of the MOSFETs etc. Usually the touch pad is just a tiny capacitor. But I could sample it reliably by increasing the sample time. The CPS module works by charging the external capacitor (the touch pad, or in my case, the isolated light bulb and disconnected MOSFETs source and drains), which is changed if your finger gets near it. The charging time is counted with timer 1. Additionally you can setup timer 0 to gate timer 1 and to generate interrupts, but I simply used a delay loop. See the files section for the MPLAB-X project file, source code and KiCad circuit diagram.

The circuit diagram:

My prize entry video:

The source code is just one file, main.c, and might help you, if you want to experiment with the CapSense module yourself:

// light bulb blinker with cap sense on/out switch at the light bulb
// ideas for the CapSense code from
// PIC16F1823 Configuration Bit Settings
// 'C' source line config statements
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = ON       // Power-up Timer Enable (PWRT enabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)
#define _XTAL_FREQ 32000000
#include <xc.h>
#include <stdint.h>
uint16_t readCapSense()
    uint16_t result;
    // clear timer 1 value and start timer
    TMR1 = 0;
    TMR1ON = 1;
    // wait 100 ms for measurement
    // turn off timer 1 and return measured value
    TMR1ON = 0;
    result = TMR1;
    return result;
void lampOff()
    PORTC = 0b00001000;
void lampOn()
    PORTC = 0b00010000;
int main()
    static uint16_t touchCalibration;
    static uint8_t i;
    static uint8_t on;
    // configure 8 MHz internal oscillator and enable 4x PLL
    OSCCON = 0xF0; 
    // wait a bit until the internal oscillator and PLL is stable
    // the datasheet says 2 ms for the PLL, but we are not in a hurry
    // disable analog inputs, all pins digital
    ANSELA = 0;
    ANSELC = 0;
    // disable ADC module
    ADCON0 = 0;
    // use RA4 as analog input, all other pins are output
    TRISA = 1 << 4;
    TRISC = 0;
    ANSA4 = 1;
    // enable CPS module:
    // use internal oscillator voltage references
    // high range: charge/discharge current is 18 uA
    CPSCON0 = 0b10001100;
    // select channel CPS3 
    CPSCON1 = 0b11;
    // timer 1 is the frequency counter for CPS
    // TMR1 capacitive sensing osc, prescaler 1/1,
    // dedicated osc disabled,no synch,timer1 enabled
    T1CON = 0b11000101;
    // initial short blinking to discharge stray capacitance
    for (i = 0; i < 4; i++) {
    // read calibration value on start and set with threshold
    // threshold very much depends on the construction
    // for just a touch pad the values can be 100 times greater
    // set a breakpoint in readCapSense and check the values in the debugger
    touchCalibration = readCapSense() - 5;
    on = 0;
    while (1) {
        // turn lamp on for half a second, if flag is set
        if (on) {
        // check the lamp touch for half a second, while lamp is turned off
        for (i = 0; i < 4; i++) {
            // lamp is touched
            if (readCapSense() < touchCalibration) {
                // wait until sensor is released
                while (readCapSense() < touchCalibration);
                // invert lamp flag and break loop for fast turn-on
                on ^= 1;
    return 0;