Tutorial 22: Calibration with Arduino
Arduino Course for Absolute Beginners
Calibration with Arduino
The world is a busy place. Sometimes in order to get the right kind of information you need to block out all the noise and focus on just one thing or a small range of things. This focusing process is a form of calibration.
In this example, you calibrate a sensor by exposing it in the first 5 seconds of the sketch to the highest and lowest inputs that you want to monitor. You are basically tuning the sensor.
We do this by adjusting the position of the potentiometer knob for five seconds when the program is initiated. The code inside the setup() encodes the highest and the lowest voltages that occur during this calibration period. These minimum and maximum values are used for the remainder of the sketch to map sensor readings to a new range. Basically, the sketch ignores any input higher or lower than the initial readings.These mapped inputs are used to set the brightness of an LED.
Why do this? Wouldn’t it make more sense to use the full range of the sensor? Good question.
Think for a moment about how your microcontroller records inputs at an analog pin. If there is 0 voltage at the pin, the microcontroller returns 0 with the analogRead() function. If 5 volts are at the analog pin, then analogRead() reports 1023.
So if you want the full scale at the analog pin you need a sensor that will vary from 0 to 5 volts. Even if your sensor is capable of a 5-volt swing, what if the inputs required to get the 5-volt swing are out of the range for your application.
Take for example a light sensor in a dark cave that must monitor light levels produced by the flashlights of miners passing by. The sensor sends a signal to a computer station and says on a scale from dark to bright how much light is being produced, with dark being pitch black and bright being a fully charged flashlight. A fully charged flashlight might not be bright enough to make your sensor change voltage at a range from 0 to 5 volts – maybe it is more like 0 to 3 volts (though one might argue you need a different light sensor). When you read the voltage at the analog pin, there will be lots of unused range – which is fine, but you want to make sure your program lets the computer station know that a change of 3 volts is fully bright.
Another example where this type of calibration makes sense is when any input higher than a certain level will automatically be the highest you care to know about.
Say you have a light sensor attached to a widget, and when the light in a room is turned on, then you want the widget to do something. There is no need to monitor levels of ambient light brighter than what your sensor will experience when the light is on – so whatever reading you get when someone shines a bright flashlight directly at your sensor will be bound by the maximum the sensor would experience with the room light turned on. Note that this does not prevent the widget from “thinking” that the light is on, but it confines the input to within the range you programmed it to handle.
A more abstract way to think about this is imagining a curved grade on a college exam. For example, pretend an exam has 100 questions, but the highest scoring student only answers 88 questions correct. A professor may curve the scoring so that the new 100% score is equivalent to having 88 questions correct.
As you work through this program I am sure you will think of many other ways to accomplish a task like this. This lesson is meant to stimulate thinking about sensor ranges, and what ranges are important to you and how to focus on the ranges that will be most useful for your application.
You Will Need
- LED (1)
- 220-Ohm Resistor (1)
- Potentiometer (1) any resistance
- Jumper Wires (3)
- Alligator Clip (1)
Step-by-Step Instructions
- Place the potentiometer into the breadboard.
- Run a jumper wire from the 5-volt pin of the Arduino to either one of the outside pins of the potentiometer.
- Run another jumper wire from one of the ground pins on the Arduino (labeled GND) to the other outside pin of the potentiometer.
- Run the final jumper wire from pin A0 on the Arduino to the middle pin of the potentiometer.
- Place one end of the 220-ohm resistor into pin 9.
- Place the short leg of the LED into the ground pin.
- Connect the long leg of the LED to the other end of the resistor with the alligator clip.
- Plug the Arduino into your computer.
- Open up the Arduino IDE.
- Open the sketch for this section.
- Click the Verify button on the top left side of the screen. It will turn orange and then back to blue once it has finished.
- Click the Upload button (next to the Verify button). It will turn orange and then back to blue once it has finished.
- Immediately after uploading the sketch, wait for the LED at pin 13 to turn on. Once it does, adjust the knob of the potentiometer back and forth, but not full scale. This is the calibration period, where the highest and lowest values are being recorded.
- After the LED at pin 13 turns off, adjust your potentiometer. Even though you don’t turn the knob full scale, the LED will still go from full dark to full bright.
This image designed with Fritzing.
The Arduino Code
/*
Calibration
Demonstrates one technique for calibrating sensor input. The
sensor readings during the first five seconds of the sketch
execution define the minimum and maximum of expected values
attached to the sensor pin.
The sensor minimum and maximum initial values may seem backwards.
Initially, you set the minimum high and listen for anything
lower, saving it as the new minimum. Likewise, you set the
maximum low and listen for anything higher as the new maximum.
The circuit:
* Analog sensor (potentiometer will do) attached to analog input 0
* LED attached from digital pin 9 to ground
created 29 Oct 2008
By David A Mellis
modified 30 Aug 2011
By Tom Igoe
http://www.arduino.cc/en/Tutorial/Calibration
This example code is in the public domain.
*/
// These constants won't change:
const int sensorPin = A0; // pin that the sensor is attached to
const int ledPin = 9; // pin that the LED is attached to
// variables:
int sensorValue = 0; // the sensor value
int sensorMin = 1023; // minimum sensor value
int sensorMax = 0; // maximum sensor value
void setup() {
// turn on LED to signal the start of the calibration period:
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
// calibrate during the first five seconds
while (millis() < 5000) {
sensorValue = analogRead(sensorPin);
// record the maximum sensor value
if (sensorValue > sensorMax) {
sensorMax = sensorValue;
}
// record the minimum sensor value
if (sensorValue < sensorMin) {
sensorMin = sensorValue;
}
}
// signal the end of the calibration period
digitalWrite(13, LOW);
}
void loop() {
// read the sensor:
sensorValue = analogRead(sensorPin);
// apply the calibration to the sensor reading
sensorValue = map(sensorValue, sensorMin, sensorMax, 0, 255);
// in case the sensor value is outside the range seen during calibration
sensorValue = constrain(sensorValue, 0, 255);
// fade the LED using the calibrated value:
analogWrite(ledPin, sensorValue);
}
Discuss the Sketch
This program begins by declaring and initializing a couple Arduino pins and setting them as constants. The other variables it creates will be used to store the highest and lowest sensor value ranges:
// These constants won’t change: const int sensorPin = A0; // pin that the sensor is attached to const int ledPin = 9; // pin that the LED is attached to // variables: int sensorValue = 0; // the sensor value int sensorMin = 1023; // minimum sensor value int sensorMax = 0; // maximum sensor value
This programmer made very descriptive and logical names for the variables – this is something we should all aspire to.
The setup() in this function is where the business of calibrating our sensor happens. While we are not accustomed to running much code in setup(), we must consider that the calibration process will only occur once at the beginning of the program – making the setup() the ideal location.
The first thing we do is to let the user know “Hey – I am in calibration mode!” – a simple way to do this is to turn on an LED:
// turn on LED to signal the start of the calibration period: pinMode(13, OUTPUT); digitalWrite(13, HIGH);
Next we start taking readings for five seconds and record the highest and lowest values. Why five seconds? It is just an arbitrary number the programmer used – it could be any length you desire. An easy way to implement a five-second window is using the while statement.
Think of the while statement just like a for loop. A for loop continues over and over until a certain condition is met and increments a counter along the way. The while statement also continues over and over, but does not have a built-in counter – it uses an “outside” condition to stop it. Let’s take a close look at the condition in this while statement:
while (millis() < 5000) {
Recall that the millis() function returns in milliseconds the elapsed time from when the program first began. This condition ensures that the elapsed time is less than 5000 milliseconds. In the first five seconds all the code following the curly brackets of the while statement is executed – but as soon as five seconds has passed, the condition will no longer be true and the program moves to the next block of code.
So what code is running during the first five seconds?
// calibrate during the first five seconds
while (millis() < 5000) {
sensorValue = analogRead(sensorPin);
// record the maximum sensor value
if (sensorValue > sensorMax) {
sensorMax = sensorValue;
}
// record the minimum sensor value
if (sensorValue < sensorMin) {
sensorMin = sensorValue;
}
}
The first thing to do is record the current sensor value using analogRead(), and assign it to a variable called sensorValue:
sensorValue = analogRead(sensorPin);
Nothing new here – you are accustomed to recording sensor values.
Next we want to check if this is the highest value we have recorded thus far. Keep in mind that when this code is running, the user is adjusting the input to the sensor. In our case, we are simulating a sensor by adjusting the potentiometer a little bit, but it just as well could be a sensor registering ambient light levels or fluctuating temperatures. We use an if statement to capture the highest input:
// record the maximum sensor value
if (sensorValue > sensorMax) {
sensorMax = sensorValue;
}
Here the condition asks “Is the current sensor reading greater than the last sensor reading?” If this is the case, we assign the current sensor reading to the sensorMax variable. This process ensures that the greatest sensor reading encountered during the calibration process is set as the upper range.
The next if statement does the same thing, but it looks for the lowest sensor reading:
// record the minimum sensor value
if (sensorValuem < sensorMin) {
sensorMin = sensorValue;
}
Here the condition asks “Is the current sensor reading less than the lowest sensor reading?” If yes, assign the current sensor reading to the sensorMin variable. This process ensures that the lowest sensor reading encountered during the calibration process is set as the lower range.
This will happen over and over for five seconds, when all is said and done, you will have a new sensorMin and sensorMax value to work with for the duration of the sketch.
Now that the calibration is complete we need to notify the user – let’s turn off the LED:
// signal the end of the calibration period digitalWrite(13, LOW);
This completes the setup() portion of the program. A lot takes place in this setup() – which is unusual compared to what we typically see, but makes perfect sense for this application.
Now I move on to the loop(). The first thing to do is measure the voltage at analog pin 0:
// read the sensor: sensorValue = analogRead(sensorPin);
Once the measurement is taken, it’s mapped to the calibrated range that was established in setup(). The map() function is used to accomplish this:
// apply the calibration to the sensor reading sensorValue = map(sensorValue, sensorMin, sensorMax, 0, 255);
But what happens if the sensor records an input outside the range of sensorMin and sensorMax that was established during the setup()? To be clear – let’s say the maximum value we recorded at the analog pin was 500 and the minimum value was 150. What will happen if a reading of 728 is encountered? That is way over the maximum range!
The reason for the calibration process is to focus our sensor on a specific range of inputs, therefore, we must guard against readings that deviate from this scale of interest. To do this we use the constrain() function:
sensorValue = constrain(sensorValue, 0, 255);
The constrain() function takes three arguments 1) the value in question 2) a lowValue 3) a highValue. If the value in question is larger than the highValue, constrain() will return the highValue – capping the size. Likewise, if the value in question is lower then the lowValue, constrain will return the lowValue – capping the low end.
In the setup(), the input was focused on a specific range. In the loop(), we make sure any outliers of that range are capped at the specified maximum and minimum values. This completes our calibration.
Now let’s use the calibrated data to adjust an output. We use analogWrite() to set the brightness of an LED attached to pin 9:
// fade the LED using the calibrated value: analogWrite(ledPin, sensorValue);
A recap of this program:
- For five seconds measure a sensor for a low and high value to establish the desired range.
- Use this range to map future sensor inputs.
- Constrain the mapped values to the previously established min and max.
- Adjust an LED based on the mapped and constrained values.
- Repeat till the cows come home.
Try On Your Own
- Write a program that allows you to press a pushbutton in order to restart the calibration process. You will need to move the calibration code from the setup() to the loop().


