LED Strip Light Switch On with Dimmer, Fade Control, and Time On Delay

I realize for some of you this may sound basic but after weeks of reading and a lot of trial and error I decided it was time to ask for some help.

I want to create a simple controller for a white LED strip light that has the following functions:

  1. Button on (later motion sensor or magnetic switch)
  2. Button to control how long the light stays on (ie. 15, 30, 45, 60 min) - for every click of the button it will go through each option; unless someone has a better suggestion?
  3. Fade amount with adjustable amount of fade time (either with a pot or in increments with a button)
  4. Dimmer pot to control brightness level (I have had a difficult time getting the fade to fade gradually to the dimmer set point)

Wish List later down the road: Debounce Stored Settings Avoid Using Delays Use Sin/Cos Function for Fade (Even the Cos formula I've used doesn't always start at 0 so any other options for a smooth fade would be greatly appreciate - maybe there's one that I haven't tried).

It sounds like it should be simple to accomplish but I have only "successfully"gotten the "Button on" and "Dimmer pot" to work together. Every time I add in another element it simply does not work as it should. I'm looking for any suggestions or help at this point (ultimately I haven't grasped how to incorporate multiple functions despite the tutorials and trial and error).

Here is what I have thus far (yes, I realize it isn't much).

const int buttonPin = 7;  // assign button pin location
const int ledPin = 9; // assign led pin location
const int dimmerControl = A0;  // assign dimmer pin location

int lastButtonState = LOW; 
int ledState;

int dimmerRead;
int dimmerValue;

int ledMin;

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);

}
void loop() {
  button1();
  dimmer();
}
void dimmer() {
  dimmerRead = analogRead(dimmerControl);
  dimmerValue = map(dimmerRead, 0, 1024, 0, 255);
  analogWrite(ledPin, dimmerValue);
}

void button1() {
  int buttonState = digitalRead(buttonPin);
  if (buttonState == LOW && buttonState != lastButtonState)
  {
    if (ledState == dimmerValue)
    {
      ledState = ledMin;
    }
    else
    {
      ledState = dimmerValue;
    }
  }
  analogWrite(ledPin, ledState);
  lastButtonState = buttonState;
}
  dimmerValue = map(dimmerRead, 0, 1024, 0, 255);

That's a rather expensive way to divide by 4.

I don't understand why you call both functions, unconditionally, in loop, when each can assign a different value to the pin.

You have far too many global variables.

I would consider using switches, not buttons. Buttons are for keep shirts from flapping around.

I’m not going to attempt to write your code for you…

Take it one step at a time.

And, it might be worthwhile learning to write [u]functions[/u]. For example, you can write a function to dim-up the light. Then, your main loop simply calls that function when you need it. It makes your main loop simpler and easier to read & modify… Once your function is working, you don’t have to mess with it… And, it makes it easier to write & debug when you can debug each function separately.

Speaking of debugging… Take advantage of the serial monitor to send messages or variables to your computer to “see” what the sketch is doing.

  1. Button to control how long the light stays on (ie. 15, 30, 45, 60 min) - for every click of the button it will go through each option; unless someone has a better suggestion?

That’s fine, but you need some kind of user feedback. Either a separate button for each delay time, or 4 LEDs to indicate the selected delay time, etc.

  1. Fade amount with adjustable amount of fade time (either with a pot or in increments with a button)

Again, with button user feedback is “difficult”. You can use a button, but somehow you need to show the user the setting.

  1. Dimmer pot to control brightness level (I have had a difficult time getting the fade to fade gradually to the dimmer set point)…

…Use Sin/Cos Function for Fade (Even the Cos formula I’ve used doesn’t always start at 0 so any other options for a smooth fade would be greatly appreciate - maybe there’s one that I haven’t tried).

A sine or cosine is the opposite of what you want (although you might be able to “invert” it). You want something exponential that makes small changes (or slower changes) at low levels and bigger changes (or faster changes) as you approach full brightness.

This may not be of any help to you, but I’ve noticed when the fade is slow (imperceptible or barley perceptible) then the fade-curve is not critical. (The same is also probably true with a very-fast fade time.)

Debounce

Don’t wait too long for that… Your button-pushes will be erratic if you don’t debounce. :wink:

Avoid Using Delays

Don’t wait for that either… You can’t read a button press (or do anything else) during the delay time.

My objections with your current code:

  • Why you are trying to set the PWM duty cycle (analogWrite) twice?

- It was somehow hard to figure out what do you want to do with this code. Are you trying to make a "toggle button" with the dimming feature? If you want that, then try this code (with some improvements):

const byte buttonPin = 7;  // assign button pin location
const byte ledPin = 9; // assign led pin location
const byte dimmerControl = A0;  // assign dimmer pin location
// Don't worry, the sketch will still work. The "byte" type takes half of memory space of an "int" type.

byte lastButtonState = LOW; // This variable stores a "logic value", so a tiny space in memory is required.
// ledState now it's an unused variable.

// dimmerRead is only used by the function dimmer, so it has no sense being here.
byte dimmerValue;
// For the analogWrite function, the second parameter has a valid range of values from 0 to 255; and a single byte can hold that range just right.

// ledMin also has to go. Looks like this variable was only meant to store a zero.

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  // INPUT_PULLUP activates an internal pull-up resistor inside the MCU, so you can save an external resistor for a push-button.
  // This means when the button is not pressed, the pin reads HIGH.

}
void loop() {
  dimmer();
  button1();
}
void dimmer() {
  int dimmerRead = analogRead(dimmerControl);
  dimmerValue = dimmerRead / 4; // Faster than map function, but leads to the same result.
  // analogWrite(ledPin, dimmerValue); unneccesary
}

void button1() {
  byte buttonState = digitalRead(buttonPin); // Read line 6 comment if you have questions in this line.
  if (buttonState == LOW) // If the button is pressed
  {
    lastButtonState = ! lastButtonState;
    // Invert it's logical value. In other words: if it's LOW, change it to HIGH; and vice versa.
  }
  if (lastButtonState = HIGH) { // If it's meant to be on
    analogWrite(ledPin, dimmerValue); // Apply the dimming
  } else { // If not
    digitalWrite(ledPin, LOW); // Turn off completely the LED.
    // Doing this is legit, even if the pin is used to PWM.
  }
}