Need help with capacitive button

Hi,

I have a LED-strip that I need to be able to turn on and off, and also be able to turn the brightness up and down. I want to turn the brightness up while I hold the “button”. This is already working ok, and the on/off function is working fine.

The problem is when i hold, because if the light is ON, I want to be able to adjust the brightness without the lights ever turning OFF. Currently it turns OFF before it starts to adjust brightness, and I have to turn the light OFF and then hold the button for it to start adjusting.

And part of the troubleshooting problem is that I am not using a button, but rather a capacitive sensor, where I use either aluminum foil or just an open wire as the “touch-button” (see Arduino Playground - CapacitiveSensor ). This makes it a bit harder for an amateur to find the solution online.

Please let me know if you need more info, this is my first time asking for help on a forum so I’m not sure what more you need.
Here is my code:
(sorry if it’s messy)

edit: And another small problem I have is that when I release the button after adjusting, it wont settle at the current brightness, but rather the next one.

#include <CapacitiveSensor.h>

int rLed=9;
int gLed=10;
int bLed=11;

int buttonState=0;
int lastButtonState=0;

int lightOn=0;
int brightness=1;
unsigned long startTime=0;

CapacitiveSensor   cs_4_2 = CapacitiveSensor(4, 2);

void setup(){
  cs_4_2.set_CS_AutocaL_Millis(0xFFFFFFFF);
  Serial.begin(9600);
}

void loop(){

  //touch;
  long start = millis();
  long total1 =  cs_4_2.capacitiveSensor(10);

  Serial.print(millis() - start);        // check on performance in milliseconds
  Serial.print("\t");                    // tab character for debug window spacing

  Serial.print(total1);                  // print sensor output 1
  Serial.print("\t");

  Serial.print(brightness);              //These are just so I have a clue on whats going on
  Serial.print("\t");

  Serial.print(lightOn);
  Serial.println("\t");

  delay(10);
  
  if (total1 > 1000) {                   //this is the value that gives me good results
    buttonState = 1;                     //it measures the capacitance between two ports
  }                                      //or something, and I don't know what this number 
  else if(total1<1000) {                 //means, but it works
    buttonState = 0;
  }



  if(buttonState==1 && lastButtonState==1){           //if the "button" is held down
    if(lightOn==1 && (millis()-startTime)>500){       //and if the light is on, and held for more than 0.5 sec
      brightness=brightness+50;
      delay(500);
                                                      
      if(brightness > 255){
        brightness=1;
      }
    }
  }
  
  if(buttonState==1 && lastButtonState==0){       //if the button is just pressed once
    lightOn=!lightOn;
    startTime=millis();

    delay(10);
  }
  lastButtonState = buttonState;

  if(lightOn==1){
    analogWrite(rLed, brightness);
    analogWrite(gLed, brightness);
    analogWrite(bLed, brightness);
  }
  
  else{
    analogWrite(rLed, 0);
    analogWrite(gLed, 0);
    analogWrite(bLed, 0); 
  }
}

Leonardie:

     if(brightness > 255){

brightness=1;
      }

A brightness of 1 is as good as off.. That may be part of your problem.

  if(buttonState==1 && lastButtonState==0){       //if the button is just pressed once

Comment is wrong. This is the situation when the button gets just touched.

Do look up and make sure you thoroughly understand the state change detection example of the IDE.

Thanks for the response. The brightness at 1 is intentional, so that when I cycle through the levels, it wont turn completely off.

As for the comment, you're right, just bad at adding comments that is.

But I think that if loop is part of the problem, because I only want it to run that specific loop if the "hold" loop is already executed.. i think.

I can't find a way to make it start the hold loop without it turning off/on the light first.

And I tried reading the state change detection, but I don't see how to implement that in my code.

Is there another place i should put the "lightOn=!lightOn" instead?

That lastButtonState part IS your state change detection.

Maybe you should just start by writing down:

  • when button becomes pressed (state change), what should my code do?
  • when the button is pressed (no state change, just pressed), what should my code do?
  • when button becomes released (state change), what should my code do?
  • when the button is released (no state change, just pressed), what should my code do?

Yeah, that makes sense. Thanks for the good advice, I'll try to experiment some more.

Okay, so I fixed both problems now. I’m just posting this in case someone else is having the same problem.

The light was turning on/off regardless if I had been doing the “hold” loop. I fixed this by simply adding the “check”-value, so that it couldn’t change state if the hold-loop was done. Both buttonState and lastButtonState (no state change) had to be 0 in order to turn on/off the light again.

And the problem where the light would always cycle to the next level after I released the button, I fixed by “pre-showing” the brightness inside the hold-loop, before the delay. There is probably a more efficient way to do this, but it worked, so I’m happy.

Here is my finished code:

#include <CapacitiveSensor.h>

int rLed=9;
int gLed=10;
int bLed=11;

int buttonState=0;
int lastButtonState=0;

int lightOn=0;
int brightness=20;
int level=0;
unsigned long startTime=0;
int check=0;

CapacitiveSensor   cs_4_2 = CapacitiveSensor(4, 2);

void setup(){
  cs_4_2.set_CS_AutocaL_Millis(0xFFFFFFFF);
  Serial.begin(9600);
}

void loop(){

  //touch;
  long start = millis();
  long total1 =  cs_4_2.capacitiveSensor(10);

  Serial.print(millis() - start);        // check on performance in milliseconds
  Serial.print("\t");                    // tab character for debug window spacing

  Serial.print(total1);                  // print sensor output 1
  Serial.print("\t");

  Serial.print(brightness);              //These are just so I have a clue on whats going on
  Serial.print("\t");

  Serial.print(lightOn);
  Serial.println("\t");

  delay(10);
  
  if (total1 > 1000) {                   //this is the value that gives me good results
    buttonState = 1;                     //it measures the capacitance between two ports
  }                                      //or something, and I don't know what this number 
  else if(total1<1000) {                 //means, but it works
    buttonState = 0;
  }


  if(lightOn==1){
    analogWrite(rLed, brightness);
    analogWrite(gLed, brightness);
    analogWrite(bLed, brightness);
  }


  if(buttonState==1 && lastButtonState==0){     //if the button becomes pressed
    startTime=millis();
    delay(10);
  }

  if(buttonState==1 && lastButtonState==1){           //if the "button" is held down
    if(lightOn==1 && (millis()-startTime)>1000){       //and if the light is on, and held for more than 0.5 sec
      level++;
      
if(level >= 5){level=1;}
if (level==1){brightness=255;}
if (level==2){brightness=100;}
if (level==3){brightness=50;}
if (level==4){brightness=20;}

      check=1;  
    analogWrite(rLed, brightness);
    analogWrite(gLed, brightness);
    analogWrite(bLed, brightness);       
      delay(1000);  
    }
  }
  
  if(buttonState==0 && lastButtonState==0){            //if the button is not pressed and no state change
    check=0;
  }

  if(buttonState==0 && lastButtonState==1 && check==0){   //button is being released, and the check is there to make sure the "hold" 
    lightOn=!lightOn;                                     //function was not just executed
  }

  lastButtonState = buttonState;
  
  if(lightOn==0){
    analogWrite(rLed, 0);
    analogWrite(gLed, 0);
    analogWrite(bLed, 0); 
  }     
}

If your sketch never runs for very long (25 days) then your millis time code won't bug which is fine.

But if you want to get in the habit of timed code that can run for months, always use unsigned variables.
In your case, unsigned long instead of long.

If/when you use micros() instead of millis(), unsigned long micros is good for 70-some minute intervals where using long variables will give you bugs in half that time.

Another good Arduino habit is to use byte variables instead of int variables for small numbers like for pins.
An Uno has 2048 bytes of RAM for -everything- and int uses 2 bytes.

These are little things that can give you problems later, this reply is just a little head's up, okay?

Even better for pin numbers: define them as const byte - and it doesn't even take up any RAM. It also will make the compiler complain in case you accidentally try to assign a new value to a pin number, which of course should never change during runtime.

Thanks alot! I will definitely implement all that in my code now that I know.

The project is a bedlight that I want to run for some time after all.

wvmarle:
Even better for pin numbers: define them as const byte - and it doesn't even take up any RAM. It also will make the compiler complain in case you accidentally try to assign a new value to a pin number, which of course should never change during runtime.

Const byte is better for pin numbers but last I recall they still get copied to RAM if you don't specify PROGMEM.

Interesting. I think it depends on how the variable is used.

This compiles to exactly the same size no matter which of three methods of defining the pin is used:

#define LEDPIN 13
//const byte LEDPIN = 13;
//byte LEDPIN = 13;

void setup() {
  pinMode(LEDPIN, OUTPUT);
}

void loop() {
  digitalWrite(LEDPIN, HIGH);         // turn the LED on (HIGH is the voltage level)
  delay(1000);                        // wait for a second
  digitalWrite(LEDPIN, LOW);          // turn the LED off by making the voltage LOW
  delay(1000);                        // wait for a second
}

As the #define definitely does not take up any RAM, I conclude that the other two (the const byte and the byte declaration) also don’t, in this case likely thanks to compiler optimisations.