One Button Dimmer

Since two days I’m trying and I’m lost.

I’m trying do replicate a bought dimmer i have.

  1. You can set the time how long it takes to dim up or down (potentiometer on the device.
  2. You can set the minimum and maximum brightness (again potentiometer).
    But i want to hardcode this two functions.

The dimmer works with just one button.

  1. If light is off and short press > light goes on (fades from 0 to last state)
  2. If light is on and short press > light goes off (fades down to 0)
  3. If light is off and button held > light goes on (fades from 0 up until button released)
  4. If light is on and button held > fades up or down until button released or minimum or maximum brightness is reached. If up or down depends on the last function. If the last thing was dimming up then a button holding dims down and vice versa.

Here is something i have tried:

#include <Bounce2.h>

const byte STARS_BUTTON = 9; // D9
const byte STARS_RELAY_ON = 11; // D11
const byte STARS_RELAY_OFF = 12; // D12
const byte STARS_STATE = 14; // A0
const byte SHOWER_LIGHT_BUTTON = 10; // D10
const byte SHOWER_RELAY_ON = 7; // D7
const byte SHOWER_RELAY_OFF = 8; // D8
const byte SHOWER_LIGHT_STATE = 19; // A5
const byte SHOWER_LIGHT_PWM = 6; // D6

Bounce showerLightButton = Bounce(); // Instantiate a Bounce object
Bounce starsButton = Bounce(); // Instantiate a Bounce object

int valueBrightness = 0;    // variable to store the brightness 
int minBrightness = 4;      // minimum brightness for dimming down
int maxBrightness = 200;    // maximum brightness for dimming up
int lastBrightness = 0;     // variable to store the last brightness 
int dimSteps = 5;           // how many points to fade the LED by
unsigned long showerLightCurrentTime = 0;
unsigned long showerLightLoopTime = 0;

void showerLight() {
  if (digitalRead(SHOWER_LIGHT_STATE)==LOW) {
    showerLightCurrentTime = millis();
    if(showerLightCurrentTime >= (showerLightLoopTime + 5000)){ 
        analogWrite(SHOWER_LIGHT_PWM, valueBrightness);
        Serial.print("Brightness:");
        Serial.println(valueBrightness);
        if (valueBrightness == 0 || valueBrightness == 255) {
          dimSteps = -dimSteps; 
        }
        valueBrightness = valueBrightness + dimSteps;
    }
  } else {
    analogWrite(SHOWER_LIGHT_PWM, 0);
    digitalWrite(SHOWER_RELAY_OFF, HIGH);
    relay();
  }
}

void relay() {
  do {
    digitalWrite(SHOWER_RELAY_OFF, HIGH);
  } while (digitalRead(SHOWER_LIGHT_STATE)==LOW);
 }

void setup() {

  Serial.begin(9600); // Open serial communications and wait for port to open
    while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  pinMode(SHOWER_LIGHT_BUTTON, INPUT); // Setup the button
  // After setting up the button, setup the Bounce instance :
  showerLightButton.attach(SHOWER_LIGHT_BUTTON);
  showerLightButton.interval(10); // interval in ms

  pinMode(SHOWER_LIGHT_PWM, OUTPUT);
  pinMode(SHOWER_RELAY_ON, OUTPUT);
  pinMode(SHOWER_RELAY_OFF, OUTPUT);
  pinMode(SHOWER_LIGHT_STATE, INPUT);
  
}


void loop() {
  // Update the Bounce instance :
  showerLightButton.update();

  // Get the updated value :
  int value = showerLightButton.read();

  if ( value == LOW ) {
    showerLight();
  }
}

The Bounce2 library does not have something like “if button pressed 2 seconds”.
I tried this one: GitHub - t3db0t/Button: A big update of Tom Igoe's fork of Alexander Brevig's Button library for Arduino. Check out the Readme for all the details!
but there the “showerLight loop” does not run.

The relay is bistable and i get a feedback into pin 19 (A5) from it to see if its on or off.
So the first thing to do is to check the relay state (pin 19 high or low).
But the “relay loop” does not work.
RELAY_ON and RELAY_OFF should only be HIGH until the relay state changed.

At the moment if i push the button my LED is at full brightness immediately.
But in the console i can see counting like:

Brightness:0
Brightness:-5
Brightness:-10
Brightness:-15
Brightness:-20
Brightness:-25
Brightness:-30
Brightness:-35
Brightness:-40
Brightness:-45

At some point the LED dims down to then go back to full brightness without fading.
The numbers in the console go up infinite as long as i press the button.

I’m lost. I guess some of you coding gods to it in 15 minutes :slightly_frowning_face:

just to clarify... you are using a relay to apply power to an LED light (ledStrip?), and then fading it using PWM?

Could you describe how that is set up?

I'm using a PT4115 LED driver that has a DIM input:
http://www.micro-bridge.com/data/CRpowtech/PT4115E.pdf

The LED is self made from 3 Cree XQ-E.

The relay is just to turn the power to the driver on/off.

MrGlasspoole:
The relay is just to turn the power to the driver on/off.

I did something similar to this not long ago. It uses a non-blocking fade library (below). I know it won’t do exactly what you want, but you could try to adapt it. I tweaked a bit to sort of do what you want… and I added some serial printing so you can ‘see’ what’s happening:

/*
/*
* Fade.h
* by BulldogLowell (BulldogLowell@gmail.com)
* Created 17-June-2014
*
* adapted from: http://stackoverflow.com/questions/10963194/timed-fading-in-arduino/10966231#10966231
*
* Non-blocking Fade library allows you to set fade level of a pin and the fade speed
* Useful for projects involving LEDs that you use to illuminate around the house
*
* Create a fade object:
*
* fade fadePin(pin, timestep, min, max)
* timeStep is by default 10ms
* min is by default 0
* max is by default 255
*
* example
* fade fadePin(5) is the same as fade fadePin(5, 10, 0, 255)
*
* to change the speed once declared.
* fadePin.writeSpeed(new speed);
* to read the current speed
* fadePin.readSpeed();
*
* fadePin.read() returns the current fade level (_pwmRate);
* fadePin.write(to) defines the new endpoint of the fader;
*
* fadePin.update(); needs to called in the loop
*
* foo.update(time); is for saving time variable if you want to sync Fade objects:
*
* unsigned long a = millis();
* foo.update(a);
* bar.update(a);
* foobar.update(a);
* it is faster than reading millis() and redefining the time every update for each Fade object
*/

#include "Fade.h"
#include <Bounce2.h>

const byte ledPin = 5;
const byte buttonPin = 6;

Fade myLed(ledPin, 15, 0, 255, MILLIS_TIMER);// led1 speed 10 milliseconds(microSeconds)/step; pwm at 10 min and 255 max  (MILLIS_TIMER or MICROS_TIMER)
Bounce myButton = Bounce(); // Instantiate a Bounce object
void setup()
{
  Serial.begin(9600);
  //pinMode(13, OUTPUT);
  myButton.attach(buttonPin);
  myButton.interval(10); // interval in ms
  pinMode(buttonPin, INPUT_PULLUP);
  myLed.begin();
  Serial.println("Setup Complete...");
}

void loop()
{
  static bool activeFading = false;
  static byte lastPressState = HIGH;
  static unsigned long lastMillis = 0;
  static unsigned long displayMillis = 0;
  static byte storedSetpoint = 255;
  static int led1Increment = 5;

  myLed.update();
  myButton.update();
  byte thisPressState = myButton.read();
  
  if (thisPressState == LOW && lastPressState == HIGH)
  {
    lastMillis = millis();  // start timer when button is pressed
    Serial.println("Pressed");
  }
  else if(thisPressState == HIGH && lastPressState == LOW)  //What to do when button is released?
  {
    Serial.println("Released");
    if (!activeFading)
    {
      myLed.setTarget(myLed.getSetpoint() == myLed.getMin()? storedSetpoint : myLed.getMin()); // on off with a simple quick button press
    }
    else
    {
      activeFading = false;  // simply stop fading
    }
  }
  if(thisPressState == LOW && millis() - lastMillis > 1000UL && activeFading == false)
  {
    activeFading = true;  // if you are holding down the button for > 1 sec, start fading
  }
  if (activeFading)
  {
    fadeLight(&myLed, storedSetpoint, led1Increment);
  }
  lastPressState = thisPressState;
  if (millis() - displayMillis > 1000)
  {
    Serial.print("Current Briteness:");
    Serial.println(myLed.getCurrent());
    Serial.print("Current Setpoint:");
    Serial.println(myLed.getSetpoint());
    Serial.println();
    displayMillis += 1000;
  }
}

void fadeLight(Fade* myFade, byte &savedSetting, int &increment)
{
  static unsigned long lastUpdateMillis = 0;
  if (millis() - lastUpdateMillis > 125UL)
  {
    myFade->setTarget(constrain((myFade->getSetpoint() + increment), myFade->getMin(), myFade->getMax()));
    if(myFade->getSetpoint() == myFade->getMin() || myFade->getSetpoint() == myFade->getMax())
    {
      unsigned long catchUpDelay = millis();
      Serial.println("FLIP");
      increment *= -1;
      Serial.println(increment);
      while(millis() - catchUpDelay < 1000UL)  // let's hold here at the peak and trough in order to be able to set to min/max easily
      {
        myFade->update();
      }
    }
    savedSetting = myFade->getSetpoint();
    Serial.println(myFade->getSetpoint());
    lastUpdateMillis = millis();
  } 
}

Fade.h:

#ifndef Fade_h
#define Fade_h

#include <Arduino.h>

enum timer{
  MILLIS_TIMER, 
  MICROS_TIMER
};

class Fade
{
  public:
    Fade() {};
    Fade(int pin, uint32_t timeStep = 15, uint8_t minVal = 0, uint8_t maxVal = 255, timer timerSelect = MILLIS_TIMER);
    void begin(); 
    void setTarget(int to);
    void update();
    void update(uint32_t time);
    uint8_t getMin();
    uint8_t getMax();
    uint8_t getCurrent();
    uint32_t readSpeed();
    uint32_t writeSpeed(uint32_t time);
    uint8_t getSetpoint();
  private:
    uint8_t _min;
    uint8_t _max;
    uint8_t _targetFade;
    uint8_t _pwmRate;
    uint32_t _time;
    uint32_t _last;
    uint8_t _pin;
    bool _microsTimer;
};

#endif

Fade.cpp:

#include "Fade.h"
#include <Arduino.h>

Fade::Fade(int pin, uint32_t timeStep, uint8_t minVal, uint8_t maxVal, timer timerSelect)
{
  _pin = pin;
  _time = timeStep;
  _min = minVal;
  _max = maxVal;
  analogWrite(_pin, _min);
  _pwmRate = _min;
  _microsTimer = timerSelect;
}

void Fade::begin()
{
  analogWrite(_pin, _min);
}

void Fade::setTarget(int to)
{
  _targetFade = (uint8_t) constrain(to, _min, _max);

  this->update();
}

void Fade::update()
{
  this->update(_microsTimer? micros() : millis());
}

void Fade::update(uint32_t time)
{
  if (time - _time > _last)
  {
    _last = time;
    if (_pwmRate > _targetFade) analogWrite(_pin, --_pwmRate);
    if (_pwmRate < _targetFade) analogWrite(_pin, ++_pwmRate);
  }
}

uint8_t Fade::getSetpoint()
{
  return _targetFade;
}

uint8_t Fade::getCurrent()
{
  return _pwmRate;
}

uint32_t Fade::readSpeed()
{
  return _time;
}

uint32_t Fade::writeSpeed(uint32_t time)
{
  _time = time;
}

uint8_t Fade::getMin()
{
  return _min;
}

uint8_t Fade::getMax()
{
  return _max;
}

EDIT: added pause at peak/trough; made the function extensible

This could help.

Library is here

//By Sodom
#include <Button.h>

const byte buton = 7;
const byte led = 11;
bool ledstate=false;
int dimm=128;
int increment=5;
unsigned long ms;               // current time from millis()
unsigned long msLast;    
Button button = Button(buton, HIGH);

boolean holding = false;

void setup()
{
  button.setDebounceDelay(70);
  button.setHoldDelay(500);
  pinMode(led,OUTPUT);
 
  Serial.begin(115200);
}

void loop()
{
  ms = millis();              // record the current time
  button.listen();

  if(!holding && button.isHold()) {
    Serial.println("Button is hold");
    holding = true;
  }
  if (!holding && button.onRelease() ) {
    Serial.println("Button pressed");
    ledstate=!ledstate;
    if(ledstate){
      analogWrite(led,dimm);
    }
    else
      analogWrite(led,0);
  
  }
  else if(holding && button.onRelease()) {
  Serial.println("Button released");
  holding = false;
  increment*=-1;
  }

  if (holding&ledstate){
    Serial.println(dimm);
    if (ms - msLast >= 60){
      msLast=ms;
      dimm+=increment;
      if (dimm<10)dimm=10;
      if (dimm>255)dimm=255;
      
    }
    
    analogWrite(led,dimm);    
  }

}