What constrain() Does
int result = constrain(value, min, max);
| If value is... | result becomes... | |----------------|-------------------| | Less than min | min | | Greater than max | max | | Between min and max | value (unchanged) |
Some examples:
constrain(150, 0, 255) -> 150 (in range, no change)
constrain(-20, 0, 255) -> 0 (below min, clamped up)
constrain(300, 0, 255) -> 255 (above max, clamped down)
Why You Need It
Sensor readings spike outside expected ranges
A photoresistor that normally reads 200-800 might return 1023 if a flashlight hits it, or 0 in complete darkness. If you feed that into analogWrite() (which expects 0-255), you have a problem.
map() does not clamp its output
This surprises people. map() happily produces values outside your target range if the input is outside the expected input range. Here is the pattern you will use constantly:
int raw = analogRead(A0);
int mapped = map(raw, 200, 800, 0, 255); // could go negative or above 255
int safe = constrain(mapped, 0, 255); // now guaranteed 0-255
analogWrite(ledPin, safe);
Without constrain(), a raw reading below 200 maps to a negative number. analogWrite() with a negative value wraps to a high brightness. That bug only shows up at the edges of your sensor range and is hard to track down.
Calculations can overflow
If you are adding or subtracting from a variable (like adjusting brightness with buttons), the value can drift past its useful range:
brightness = brightness + 10;
brightness = constrain(brightness, 0, 255);
Protecting Hardware
Servo motors
Servos can be damaged if driven past their mechanical limits. constrain() acts as a safety net:
int angle = map(analogRead(A0), 0, 1023, 0, 180);
angle = constrain(angle, 10, 170); // stay away from mechanical stops
servo.write(angle);
PWM values
analogWrite() expects 0-255. If your value comes from any calculation, constrain it.
What constrain() Does NOT Do
It does not scale. constrain(500, 0, 255) returns 255, not a proportionally scaled value. That is what map() is for.
It does not wrap around. constrain(10, 0, 5) returns 5, not 0. For wrapping behavior, use modulo (%).
// Wrapping (not what constrain does):
menuIndex = menuIndex % numItems; // 0,1,2,3,0,1,2,3,...
// Clamping (what constrain does):
menuIndex = constrain(menuIndex, 0, numItems - 1); // stops at the ends
The map() + constrain() Pattern
This comes up so often it is worth memorizing:
int output = constrain(map(input, inMin, inMax, outMin, outMax), outMin, outMax);
Concrete examples:
// Potentiometer controlling LED brightness
int brightness = constrain(map(analogRead(A0), 0, 1023, 0, 255), 0, 255);
// Photoresistor controlling servo
int angle = constrain(map(analogRead(A1), 200, 800, 10, 170), 10, 170);
Common Mistakes
| Mistake | What Happens | Fix | |---------|-------------|-----| | Getting min and max backwards | Wrong results, no error | Second argument must be smaller than third | | Expecting it to scale values | constrain(1023, 0, 255) returns 255, not 64 | Use map() for scaling, constrain() for clamping | | Unsigned type problems | Negative values wrap to huge positives before constrain sees them | Do math with int, constrain, then cast to byte | | Hiding bad sensor data | Silently swallows spikes that might indicate wiring problems | Print a warning when values are out of range |