Constrain increments

Hi

I have a joystick that needs to +/- based on the position it’s pushed. The problem I now have is that a positive value of 3000 is allowed, so BYTE will not work but I also have a minimum of for instance 20.

Can I use constrain in code like I have now? I’ve put my last test in there that does now work…

if (setupActive == 0 && setting1 == 1 && memorySet == 1 & analogRead(JS_X) > 912){ // memory is set so whe can modify the speed?
  SpeedCont += 10;
  Serial.print(F("SpeedCont:  ")); Serial.println(SpeedCont);           //  debugging
} else if (setupActive == 0 && setting1 == 1 && memorySet == 1 & analogRead(JS_X) < 112){ 
  constrain(SpeedCont -= 10,0,3000);
  Serial.print(F("SpeedCont:  ")); Serial.println(SpeedCont);           //  debugging
}

I am unsure if it is even possible to limit the input like that at all?

  constrain(SpeedCont -= 10,0,3000);

constrain() returns a value but you are throwing the value away

constrain()

  constrain(SpeedCont -= 10,0,3000);

constrain() is a macro and will evaluate SpeedCont -= 10 one to three times depending on the values.

#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))

UKHeliBob:

  constrain(SpeedCont -= 10,0,3000);

constrain() returns a value but you are throwing the value away

constrain()

Thanks... I think I understand what I did wrong.

  constrain(SpeedCont -= 10,0,3000);

should be

  SpeedCont = constrain(SpeedCont -= 10,0,3000);

because it was constraining a value but not setting it to SpeedCont. Is that correct?

snewpers:
Thanks... I think I understand what I did wrong.

  constrain(SpeedCont -= 10,0,3000);

should be

  SpeedCont = constrain(SpeedCont -= 10,0,3000);

because it was constraining a value but not setting it to SpeedCont. Is that correct?

Looks OK now. Test it by writing a small program that just constrains a range of values and prints the result.

snewpers:

  constrain(SpeedCont -= 10,0,3000);

should be

  SpeedCont = constrain(SpeedCont -= 10,0,3000);

because it was constraining a value but not setting it to SpeedCont. Is that correct?

No, the decrement assignment is still happening in the macro.

If you want to use constrain, you could use

  SpeedCont -= 10;
  SpeedCont = constrain(SpeedCont, 0, 3000);

or, if you only want to catch illegal values generated by the decrement operation

  if ((SpeedCont -= 10) < 0) {
    SpeedCont = 0;
  }

Whandall:
No, the decrement assignment is still happening in the macro.

The reason for the alarm, and probably also why this

UKHeliBob:
Looks OK now. Test it by writing a small program that just constrains a range of values and prints the result.

Is because of what happens when you do use constrain() in that manner
The macro is defined as the following

#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))

so using the statement

SpeedCont = constrain(SpeedCont -= 10,0,3000);

will get expanded by the compiler/preprocessor to

SpeedCont = ((SpeedCont -= 10) < 0 ? 0: ((SpeedCont -= 10) > 3000 ? 3000 : (SpeedCont -= 10)));

expanding the shorthand if/then/else statement gives us

if ((SpeedCont -=10) < 0) {
  SpeedCont = 0;
}
else {
  if ((SpeedCont -= 10) > 3000) {
    SpeedCont = 3000
  }
  else {
    SpeedCont = (SpeedCont -= 10)
  }
}
// curly braces for clarity

Evaluating the code shows that if SpeedCont started off with a value of 50 at the start of the macro, it pop out at the other end with a value of 20, not 40 as was probably intended.
(Worse, try evaluating that if/then/else with a starting value of 20)

It drove me crazy trying to find out what was going on when using sq() a (looong)while back. sq() was a macro and I didn’t realise it.

snewpers:
Thanks... I think I understand what I did wrong.

  constrain(SpeedCont -= 10,0,3000);

should be

  SpeedCont = constrain(SpeedCont -= 10,0,3000);

because it was constraining a value but not setting it to SpeedCont. Is that correct?

No, the -= is bogus.

  SpeedCont = constrain(SpeedCont - 10, 0, 3000);

Uhm

This actually works fine, SpeedCont starts as 200, and stops at 10 and 3000, depending of course what direction I push the joystick.

SpeedCont = constrain(SpeedCont -= 10,0,3000);

Not sure if I should modify it now because there’s a bit of a discussion about the macro?

Also, the ‘-=’ I got from the arduino reference…

‘This is a convenient shorthand to perform subtraction of a constant or a variable from a variable.’

I found it here: Linktto reference

If this is wrong I’d really like to know :slight_smile:

Code I use now is:

if (setupActive == 0 && setting1 == 1 && memorySet == 1 & analogRead(JS_X) > 912){ 
  SpeedCont = constrain(SpeedCont += 1,1,1000);
  Serial.print(F("SpeedCont:  ")); Serial.println(SpeedCont);           //  debugging
} else if (setupActive == 0 && setting1 == 1 && memorySet == 1 & analogRead(JS_X) < 112){ // memory is set so whe can modify the speed maybe?
  SpeedCont = constrain(SpeedCont -= 1,1,1000);
  Serial.print(F("SpeedCont:  ")); Serial.println(SpeedCont);           //  debugging
}

Thanks!

snewpers:
Uhm

This actually works fine, SpeedCont starts as 200, and stops at 10 and 3000, depending of course what direction I push the joystick.

SpeedCont = constrain(SpeedCont -= 10,0,3000);

Not sure if I should modify it now because there’s a bit of a discussion about the macro?

See my response above.
while the code might appear to work. there will be at least one situation where SpeedCont becomes negative for one loop iteration

snewpers:
Also, the ‘-=’ I got from the arduino reference…

‘This is a convenient shorthand to perform subtraction of a constant or a variable from a variable.’

I found it here: Linktto reference

If this is wrong I’d really like to know :slight_smile:

There is nothing wrong with using “-=”. It’s just where and how you are using it that is an issue.
Again, re-read my post, especially the section where the constrain() macro gets expanded.

Try for yourself. Using SpeedCont = 2, manually expand SpeedCont = constrain(SpeedCont -= 1,1,1000);

@Darrob

Thank you for explaining this. It took me a while to grasp the macro and did a test. You were absolutely right about the values not being correct. I had the range in testing set so high that I didn't even notice it skipping 3 x the increment every time. So indeed the result was 100 - 130 - 160 etc with an increment that was set for 10.

I understand now what I did wrong and why it would never work inside the macro.

I used @Whandall's solution to get it to work; (thank you Whandall)

SpeedCont -= 10;
  SpeedCont = constrain(SpeedCont, 0, 3000);

This works fine and does not skip values.

darrob:
The reason for the alarm, and probably also why thisIs because of what happens when you do use constrain() in that manner
The macro is defined as the following

#define constrain(amt,low,high) ((amt)<(low)?(low):((amt)>(high)?(high):(amt)))

so using the statement

SpeedCont = constrain(SpeedCont -= 10,0,3000);

will get expanded by the compiler/preprocessor to

SpeedCont = ((SpeedCont -= 10) < 0 ? 0: ((SpeedCont -= 10) > 3000 ? 3000 : (SpeedCont -= 10)));

expanding the shorthand if/then/else statement gives us

if ((SpeedCont -=10) < 0) {

SpeedCont = 0;
}
else {
  if ((SpeedCont -= 10) > 3000) {
    SpeedCont = 3000
  }
  else {
    SpeedCont = (SpeedCont -= 10)
  }
}
// curly braces for clarity




Evaluating the code shows that if SpeedCont started off with a value of 50 at the start of the macro, it pop out at the other end with a value of 20, not 40 as was probably intended.
(Worse, try evaluating that if/then/else with a starting value of 20)

It drove me crazy trying to find out what was going on when using sq() a (looong)while back. sq() was a macro and I didn't realise it.
This is why I hate macros like `constrain()`. Not only are they solutions to a non-problem, but they don't even work all that well, and as you have shown, can be full of nasty surprises. Besides, the name "constrain" is ambiguous. "clamp" would be a better name; "constrain" sounds like something I would use on, say, an angle to turn `-2` into `358`, or `370` into `10`.

Just don’t be lazy. Do such things the long way. Or, if you must, write your own functions so that you don’t get surprised.