Button hold -> PWM value

Hello! I'm trying to write code for a button. If button is pressed once (or held down less than, let's say, 400ms), output will go instantly to 100%. But if I hold it down longer, then after 400ms it starts sending PWM output and after 2,4 seconds output will be 100%. If I hold it 1,4 seconds, then output will be 50%. So it first waits for 400ms and find out if button is pressed or held, then starts increasing PWM output. I tried to write code, but it was a mess at the end and I only got it switch on, when holding a button less than 400ms. Maybe somebody can help me out...

Maybe we can help you when you post your code.

The things that you need to do are: Detect a transition - from pressed to released or from released to pressed. Detect whether the transition is to pressed or to released. Record when the transition occurred. Detect whether the switch is pressed. Measure how long the switch has been pressed. Determine a PWM value based on the time. Write that value to a PWM pin. Perform these steps in the proper order.

Which part(s) do you need help with?

I tried changing it, but I don’t have previous versions.
LED goes on after power on, but even if serial monitor shows values below 400, it won’t turn off LED.

int buttonstate = 0;
unsigned long presstime;
unsigned long starttime;
unsigned long holdtime;


void setup(){
pinMode(12, INPUT);
pinMode(13, OUTPUT);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);

Serial.begin(9600);
}

void loop() {
  if(buttonstate == 0 && digitalRead(12) == LOW){
    starttime = millis();
    buttonstate = 1;
  }
  if(buttonstate == 1 && digitalRead(12) == HIGH) {
    presstime = millis();
    buttonstate = 0;
  }
  holdtime = presstime - starttime;
  Serial.println(holdtime);
  if (holdtime < 400) {
    digitalWrite(13, HIGH);
  }
}

PaulS: Measure how long the switch has been pressed.

Which part(s) do you need help with?

I think I have half of this, cause serial monitor shows correct value, but LED is ON and thinks about bigger values. :D

Nowhere do you tell the Arduino to turn the LED off, regardless of value.

PaulS: Nowhere do you tell the Arduino to turn the LED off, regardless of value.

digitalWrite(13, LOW);

Not good?

And subsequently?

Changed that part:

if (holdtime < 400) {
    digitalWrite(13, HIGH);
  } else if (holdtime >400){
    digitalWrite(13, LOW);
  }

Now this part works.
But I have no idea how to put in this PWM part, because it must read the time and send value all the time when holding a button.
At the moment it gives me button hold time after releasing the button.

At the moment it gives me button hold time after releasing the button.

Because that is when you compute it. Change when you compute the hold time.

startTime and pressTime look like the same thing to me. pressedTIme and releasedTime look like different things. Meaningful names are better than random names.

PaulS:

At the moment it gives me button hold time after releasing the button.

Because that is when you compute it. Change when you compute the hold time.

startTime and pressTime look like the same thing to me. pressedTIme and releasedTime look like different things. Meaningful names are better than random names.

I thought this way: millis(): Returns the number of milliseconds since the Arduino board began running the current program. starttime is the time, when code started running and presstime is the time when button press occurs. But pressed and released looks better, yes.

When I bring my button input to GND, serial monitor outputs 4294965199 as holdtime. What is that?

starttime is the time, when code started running

But your code:

  if(buttonstate == 0 && digitalRead(12) == LOW){
    starttime = millis();
    buttonstate = 1;
  }

Sets starttime to "now" when some combination of things is true, so it isn't "when the code started running".

The logic of reading the switch could use some improvement, too.

int currState;
int prevState = LOW;

void loop()
{
   currState = digitalRead(somePin);
   if(currState != prevState)
   {
      // A transition occurred - from pressed to released or from released to pressed
   }
   prevState = currState;
}

No confusing buttonstate variable needed, and the switch is only read once per iteration of loop. It would be in that if block that you would record the pressedTime, if the transition was to pressed.

After that if block, you'd test the state again, using the value in currState. If the switch is pressed, and pressedTime is greater then 0, now - pressedTime would be the time that the switch has been pressed. The actual time the switch is released is irrelevant.

Well, I tried to change something, but if I take my input HIGH, serial monitor outputs time difference as 4294953764.

unsigned long pressedTime;
unsigned long now;
unsigned long holdtime;
int currState;
int prevState = LOW;


void setup(){
pinMode(12, INPUT);
pinMode(13, OUTPUT);
digitalWrite(12, LOW);
digitalWrite(13, LOW);

Serial.begin(9600);
}

void loop() {
  currState = digitalRead(12);
  if(currState != prevState)
  {
    if (currState == HIGH) {
      pressedTime = millis();
    }
  }
  prevState = currState;
  if (currState != prevState) {
    now = millis();
  }
  Serial.println(now-pressedTime);

}

Sorry, if I misunderstood some explanations. English is not my native language. But... Is my code very wrong?

How is your button hardware set up? Do you have a resistor or something similar on the button pin to keep it from possibly floating and causing issues? For simple button setups, often the button pin is set high and the button connected to ground, such that the normal button state is high, and then low when the button is pushed.

zoomkat: How is your button hardware set up? Do you have a resistor or something similar on the button pin to keep it from possibly floating and causing issues? For simple button setups, often the button pin is set high and the button connected to ground, such that the normal button state is high, and then low when the button is pushed.

Good to know. Thank You for this information. My button pin is now high and when pressed, it goes LOW.

  prevState = currState;
  if (currState != prevState) {

How can currState not equal prevState? Move the assignment statement to the end of loop.

Like this?

void loop() {
  currState = digitalRead(12);
  if(currState != prevState)
  {
    if (currState == HIGH) {
      pressedTime = millis();
    }
  }
  prevState = currState;
  if (currState != prevState) {
    now = millis();
  }
  prevState = currState;
  Serial.println(now-pressedTime);
}

No, you still have:-

prevState = currState;
  if (currState != prevState) {

So that if statement is never going to do anything.

So just removing first prevState = currState; should make it work?

edit:
So I got LED ON function working. Now I also need LED OFF function. If click is less than 400ms, it must turn off. I think I must use one more variable?
And then comes harder part, analog output…

edit2:
Now I tried to use variable “on” to check if LED has been switched on. And clicking the button must switch LED ON and OFF. But it’s always on. Can anybody point me in the right direction once again?

unsigned long pressedTime;
unsigned long now;
unsigned long holdTime;
int currState;
int prevState = HIGH;
int on=0;

void setup(){
pinMode(12, INPUT);
pinMode(13, OUTPUT);
digitalWrite(12, HIGH);
digitalWrite(13, LOW);

Serial.begin(9600);
}

void loop() {
  currState = digitalRead(12);
  if(currState != prevState){
    if (currState == LOW) {
      pressedTime = millis();
    }
  }
  if (currState != prevState) {
    now = millis();
  }
  prevState = currState;
  holdTime = now-pressedTime;
  Serial.println(holdTime);
  if (holdTime > 0 && holdTime < 400) {
    if (on == 0){
      digitalWrite(13, HIGH);
      on=1;
    } else if (on == 1)  {
      digitalWrite(13, LOW);
      on=0;
    }
  }
}

And LED just stays on changing little of its brightness when bringing input pin low.

Why are you doing this:-

if(currState != prevState){
    if (currState == LOW) {
      pressedTime = millis();
    }
  }
  if (currState != prevState) {
    now = millis();
  }

and not this:-

if(currState != prevState){
    if (currState == LOW) {
      pressedTime = millis();
     now = millis();
    }
  }

And LED just stays on changing little of its brightness when bringing input pin low.

Because you have got your brackets wrong.