Capacitive Touch Lamp - Mode Problems

Project outline:

Using the capsense library, create a touch sensitive lamp that increases in brightness each time the sensor is touched. In this case, the sensor will be the metal frame of a bedside table. The lamp itself is a large LED strip, and will be mounted on the bed frame to provide a nice gentle back light.
It starts in mode 0 (which is off). Each time a touch is sensed, the mode value is increased by +1. Once the mode variable hits a value of 4, it rolls over to a value of 0 (OFF).

The Problem

  • The code compiles okay. The lamp starts OFF, but after the first touch the mode is stuck on 3 (full power). This can be seen using the serial monitor.
  • Also, not critical, but I'd like to learn how to do the following. use millis() instead of delay() as I really don't like having breaks in my code.

This is my first project, I'm just learning how to code so please excuse any sloppy code. I've tried to write lots of notes to make it easier to understand. Cheers.

EDIT
Changed byte mode = 0; to int mode = 0;
Changed mode comparisons

int LEDPin = 11;       //PWM Output pin for LED 
int capSensePin = 2;   //Pin to attach to capacitive sensor
int mode = 0;         //Determines LED brightness. 0 is off. Varies between 0 and 255.
int touchThreshold = 50; //Minimum capacitive touch value  in order to trigger next  mode

void setup(){
  Serial.begin(9600);
  pinMode(LEDPin, OUTPUT);  //Set LEDPin to output mode
}

void loop(){

  if (readCapacitivePin(capSensePin) > touchThreshold)   //If the value of capSensePin exceeds touchThreshold then do...
  {
  delay(250);                            //Button Debounce. How would I remove this break using millis()??
  mode++; //increase value of mode by 1

//This next section outlines the different LED brightness levels
  if (mode == 0) analogWrite(LEDPin, 0);
  if (mode == 1) analogWrite(LEDPin, 100);
  if (mode == 2) analogWrite(LEDPin, 150);
  if (mode == 3) analogWrite(LEDPin, 255);
  if (mode > 3) mode = 0;  //If the value for mode is equal to 4 then set value of mode to 0. 
  Serial.print("The current mode is..."); //Serial monitor to bebug mode increases
  Serial.println(mode);  //print value of mode to seial monitor
 
  }

  // THIS POINT ONWARD I DIDNT WRITE.
  // Every 500 ms, print the value of the capacitive sensor
  if ( (millis() % 500) == 0){
    Serial.print("Capacitive Sensor on Pin 2 reads: ");
    Serial.println(readCapacitivePin(capSensePin));
  }
}

// readCapacitivePin
//  Input: Arduino pin number
//  Output: A number, from 0 to 17 expressing
//          how much capacitance is on the pin
//  When you touch the pin, or whatever you have
//  attached to it, the number will get higher
//  In order for this to work now,
// The pin should have a 1+Megaohm resistor pulling
//  it up to +5v.
uint8_t readCapacitivePin(int pinToMeasure){
  // This is how you declare a variable which
  //  will hold the PORT, PIN, and DDR registers
  //  on an AVR
  volatile uint8_t* port;
  volatile uint8_t* ddr;
  volatile uint8_t* pin;
  // Here we translate the input pin number from
  //  Arduino pin number to the AVR PORT, PIN, DDR,
  //  and which bit of those registers we care about.
  byte bitmask;
  if ((pinToMeasure >= 0) && (pinToMeasure <= 7)){
    port = &PORTD;
    ddr = &DDRD;
    bitmask = 1 << pinToMeasure;
    pin = &PIND;
  }
  if ((pinToMeasure > 7) && (pinToMeasure <= 13)){
    port = &PORTB;
    ddr = &DDRB;
    bitmask = 1 << (pinToMeasure - 8);
    pin = &PINB;
  }
  if ((pinToMeasure > 13) && (pinToMeasure <= 19)){
    port = &PORTC;
    ddr = &DDRC;
    bitmask = 1 << (pinToMeasure - 13);
    pin = &PINC;
  }
  // Discharge the pin first by setting it low and output
  *port &= ~(bitmask);
  *ddr  |= bitmask;
  delay(1);
  // Make the pin an input WITHOUT the internal pull-up on
  *ddr &= ~(bitmask);
  // Now see how long the pin to get pulled up
  int cycles = 16000;
  for(int i = 0; i < cycles; i++){
    if (*pin & bitmask){
      cycles = i;
      break;
    }
  }
  // Discharge the pin again by setting it low and output
  //  It's important to leave the pins low if you want to 
  //  be able to touch more than 1 sensor at a time - if
  //  the sensor is left pulled high, when you touch
  //  two sensors, your body will transfer the charge between
  //  sensors.
  *port &= ~(bitmask);
  *ddr  |= bitmask;
  
  return cycles;
}
    if (mode = 0) analogWrite(LEDPin, 0);
    if (mode = 1) analogWrite(LEDPin, 100);
    if (mode = 2) analogWrite(LEDPin, 150);
    if (mode = 3) analogWrite(LEDPin, 254);
    if (mode == 4) mode = 0;

Spot the difference between these lines ?
Only one of them tests the value of mode.

There is a difference between using single = and double ==
A single one will assign a value to mode and a double will check the value against mode. Your code is correct for mode 4 but the others are wrong.

  if (mode = 0) analogWrite(LEDPin, 0);
  if (mode = 1) analogWrite(LEDPin, 100);
  if (mode = 2) analogWrite(LEDPin, 150);
  if (mode = 3) analogWrite(LEDPin, 254);
  if (mode == 4) mode = 0; //If the value for mode is equal to 4 then set value of mode to 0.

Once you get this working okay it might be nice to add code to ramp the brightness up to the next level instead of doing an instant change in brightness.

Riva:
There is a difference between using single = and double ==
A single one will assign a value to mode and a double will check the value against mode. Your code is correct for mode 4 but the others are wrong.

  if (mode = 0) analogWrite(LEDPin, 0);

if (mode = 1) analogWrite(LEDPin, 100);
 if (mode = 2) analogWrite(LEDPin, 150);
 if (mode = 3) analogWrite(LEDPin, 254);
 if (mode == 4) mode = 0; //If the value for mode is equal to 4 then set value of mode to 0.



Once you get this working okay it might be nice to add code to ramp the brightness up to the next level instead of doing an instant change in brightness.

Thank you both for your help.

  if (mode == 0) analogWrite(LEDPin, 0);
  if (mode == 1) analogWrite(LEDPin, 100);
  if (mode == 2) analogWrite(LEDPin, 150);
  if (mode == 3) analogWrite(LEDPin, 255);
  if (mode > 3) mode = 0;  //If the value for mode is equal to 4 then set value of mode to 0.

Is the new code I put in. Something I noticed is that when it rolls over to 0, the led stays on. The serial monitor output suggests it IS in mode 0 but there is still an output. Any ideas why?

I love the idea of the gliding up in brightness. I'll have to have a look at that fade tutorial and see if I can figure it out from there.

  if (mode == 0) analogWrite(LEDPin, 0);
  if (mode == 1) analogWrite(LEDPin, 100);
  if (mode == 2) analogWrite(LEDPin, 150);
  if (mode == 3) analogWrite(LEDPin, 255);
  if (mode > 3) {mode = 0;
 analogWrite(LEDPin,0);
 }

This now turns it off on the fourth sensor press. It seems clumsy though. Why do I need to add the additional analogWrite(LEDPin,0) to turn off the LED?

ALSO, can anyone suggest how to remove the break from the code by using millis() ?

You are only checking the value of mode and setting the lamp output when the lamp is touched. When the value of mode rolls over and is set back to 0 no action is taken immediately unless you have the analog.write in place.

Look at the BlinkWithoutDelay example in the IDE to see how to avoid the use of delay(). The basic idea is to note a start time, do stuff, check whether the 'delay' period has elapsed and if not, do stuff until it is. The timing is done using the millis() function which does not block the execution of other code.

UKHeliBob:
You are only checking the value of mode and setting the lamp output when the lamp is touched. When the value of mode rolls over and is set back to 0 no action is taken immediately unless you have the analog.write in place.

Look at the BlinkWithoutDelay example in the IDE to see how to avoid the use of delay(). The basic idea is to note a start time, do stuff, check whether the 'delay' period has elapsed and if not, do stuff until it is. The timing is done using the millis() function which does not block the execution of other code.

Thanks for the explanation regarding why I had to include another line of analogWrite.

As for fading the light between modes, I am somewhat at a loss. The "fade" and "fading" tutorials don't seem to be what i'm looking for.

I'd like to for the lights to ramp up in brightness after each button press.

Thanks for the help thus far.

timbit1985:
As for fading the light between modes, I am somewhat at a loss. The "fade" and "fading" tutorials don't seem to be what i'm looking for.

I'd like to for the lights to ramp up in brightness after each button press.

Thanks for the help thus far.

To ramp up/down the brightness you need to have a couple extra variables. 1 will hold the target brightness you want to achieve the other will hold the current brightness. The code then increments/decrements current brightness a bit at a time trying to match it with target brightness.

int LEDPin = 11;       //PWM Output pin for LED 
int capSensePin = 2;   //Pin to attach to capacitive sensor
int mode = 0;         //Determines LED brightness. 0 is off. Varies between 0 and 255.
int touchThreshold = 50; //Minimum capacitive touch value  in order to trigger next  mode
byte targetBrightness = 0; // Set power on brightness
byte currentBrightness = 0;

void setup(){
    Serial.begin(9600);
    pinMode(LEDPin, OUTPUT);  //Set LEDPin to output mode
}

void loop(){

    if (readCapacitivePin(capSensePin) > touchThreshold) {  //If the value of capSensePin exceeds touchThreshold then do...
        delay(250);     
        //Button Debounce. How would I remove this break using millis()??
        mode++; //increase value of mode by 1

        //This next section outlines the different LED brightness levels
        if (mode > 3) mode = 0;  //If the value for mode is > 3 then set value of mode to 0. 
        if (mode == 0) targetBrightness = 0;
        if (mode == 1) targetBrightness = 100;
        if (mode == 2) targetBrightness = 150;
        if (mode == 3) targetBrightness = 255;
        Serial.print("The current mode is..."); //Serial monitor to bebug mode increases
        Serial.println(mode);  //print value of mode to seial monitor

    }

    if (targetBrightness > currentBrightness) currentBrightness++;
    if (targetBrightness < currentBrightness) currentBrightness--;
    analogWrite(LEDPin, currentBrightness);
    //Serial.println(currentBrightness);

    // THIS POINT ONWARD I DIDNT WRITE.
    // Every 500 ms, print the value of the capacitive sensor
    if ( (millis() % 500) == 0){
        Serial.print("Capacitive Sensor on Pin 2 reads: ");
        Serial.println(readCapacitivePin(capSensePin));
    }
}

// readCapacitivePin
//  Input: Arduino pin number
//  Output: A number, from 0 to 17 expressing
//          how much capacitance is on the pin
//  When you touch the pin, or whatever you have
//  attached to it, the number will get higher
//  In order for this to work now,
// The pin should have a 1+Megaohm resistor pulling
//  it up to +5v.
uint8_t readCapacitivePin(int pinToMeasure){
    // This is how you declare a variable which
    //  will hold the PORT, PIN, and DDR registers
    //  on an AVR
    volatile uint8_t* port;
    volatile uint8_t* ddr;
    volatile uint8_t* pin;
    // Here we translate the input pin number from
    //  Arduino pin number to the AVR PORT, PIN, DDR,
    //  and which bit of those registers we care about.
    byte bitmask;
    if ((pinToMeasure >= 0) && (pinToMeasure <= 7)){
        port = &PORTD;
        ddr = &DDRD;
        bitmask = 1 << pinToMeasure;
        pin = &PIND;
    }
    if ((pinToMeasure > 7) && (pinToMeasure <= 13)){
        port = &PORTB;
        ddr = &DDRB;
        bitmask = 1 << (pinToMeasure - 8);
        pin = &PINB;
    }
    if ((pinToMeasure > 13) && (pinToMeasure <= 19)){
        port = &PORTC;
        ddr = &DDRC;
        bitmask = 1 << (pinToMeasure - 13);
        pin = &PINC;
    }
    // Discharge the pin first by setting it low and output
    *port &= ~(bitmask);
    *ddr  |= bitmask;
    delay(1);
    // Make the pin an input WITHOUT the internal pull-up on
    *ddr &= ~(bitmask);
    // Now see how long the pin to get pulled up
    int cycles = 16000;
    for(int i = 0; i < cycles; i++){
        if (*pin & bitmask){
            cycles = i;
            break;
        }
    }
    // Discharge the pin again by setting it low and output
    //  It's important to leave the pins low if you want to 
    //  be able to touch more than 1 sensor at a time - if
    //  the sensor is left pulled high, when you touch
    //  two sensors, your body will transfer the charge between
    //  sensors.
    *port &= ~(bitmask);
    *ddr  |= bitmask;

    return cycles;
}

Thanks RIVA! I'd actually woken up this morning after sleeping on it, and had a similar solution. Yours is more elegant, so i'm going to use it :).

timbit1985:
Thanks RIVA! I'd actually woken up this morning after sleeping on it, and had a similar solution. Yours is more elegant, so i'm going to use it :).

I do most of my best work while sleeping :slight_smile: The code is not finished as you will maybe need to apply some delay to adjust transition speed but it should point you in the right direction.