Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

Arduino Map

Arduino map - Why it may not work exactly the way you think it does! The map function is intended to change one range of values into another range of values and a common use is to read an analogue input (10 bits long, so values range from 0 to 1023) and change the output to a byte so the output would be from 0 to 255.

TIP: map() can convert from positive ranges to negative ranges.

You would then write the following code

val = map(adc_val, 0, 1023, 0, 255);

Everything is fine 0 maps to 0, and 1023 maps to 255...

...with an even distribution (really?????).

The map() function is useful but it has a secret hiding within. Its easy enough to use (or so you think) until you look a bit deeper inside. An even distribution is what you want but does it really do that?

Testing Arduino Map

Lets write a sketch to test out exactly what it does

void setup() {

  Serial.begin(9600);

  for (int adc = 0; adc < 1024; adc++) {

    int mapped = map(adc, 0, 1023, 0, 255);
    Serial.print(adc);
    Serial.print(',');
    Serial.println(mapped);
  }
}

void loop() {
}

First part of the serial output Final part of the serial output
0,0
1,0
2,0
3,0
1007,251
1008,251
1009,251
1010,251
4,0
5,1
6,1
7,1
1011,252
1012,252
1013,252
1014,252
8,1
9,2
10,2
11,2
1015,253
1016,253
1017,253
1018,253
12,2
13,3
14,3
15,3
1019,254
1020,254
1021,254
1022,254

1023,255


In the table above the value to the left of the comma is the adc value while to the right is the mapped output value.

You can see from the table above that input is mapped to output ranges in blocks of 4 (1 output value for a range of 4 input values). This is expected since 1024/4 = 256.

The problem is the final output value has only 1 input for one output i.e. 1023 results in 255, while 1019~1022 results in 254 as output. What you really want is an even spread of values across the whole range.

To get to this point some of the other outputs must have had 5 values as inputs (you can see that for adc values 0~4 - all 5 inputs result in output of a zero.

Sketch to check Arduino map distribution

This sketch increases the value in bins array every time map returns a value - so each bins[] holds the number of times an output was created.

#define SAMPLES 1024
#define OUTPUTS 256

int bins[OUTPUTS];
const int binsize = (SAMPLES-1)/(OUTPUTS-1);

void setup() {

  Serial.begin(9600);
  Serial.println("Arduino map output distribution");
  Serial.print("Bin size: ");
  Serial.println(binsize);

  // Initialise bins
  for (int i = 0; i < OUTPUTS; i++) bins[i]=0;

  for (int adc = 0; adc < SAMPLES; adc++) {
    int mapped = map(adc, 0, SAMPLES-1, 0, OUTPUTS-1);
    bins[mapped] +=1;
  }

  for(int i=0;i<OUTPUTS;i++) {
    Serial.print("Bin: ");
    Serial.print(i);
    Serial.print(" count ");
    Serial.print(bins[i]);

    // Show bin distribution error
    if ((bins[i] != binsize)) {
      Serial.println(" ***") ;
    } else Serial.println();

  }

  // Find wrong bin count
  for(int i=0;i<OUTPUTS;i++) {
    if (bins[i]!=binsize) {
      Serial.print("Bin error: ");
      Serial.println(i);
    }
  }
}

void loop() {
}

The last part of the output shows four bins with wrong values :

Bin: 253 count 4
Bin: 254 count 4
Bin: 255 count 1 ***
Bin error: 0
Bin error: 85
Bin error: 170
Bin error: 255

So why the error for Arduino map?

The arduino map() reference has this to say:

"As previously mentioned, the map() function uses integer math. So fractions might get suppressed due to this. For example, fractions like 3/2, 4/3, 5/4 will all be returned as 1 from the map() function, despite their different actual values. So if your project requires precise calculations (e.g. voltage accurate to 3 decimal places), please consider avoiding map() and implementing the calculations manually in your code yourself."

The problem is that the upper value of 1023 is not exactly divisble by 255 so you get a slight error. So it is a fraction that is suppressed.

The code for the function is:

long map(long x, long in_min, long in_max, long out_min, long out_max) {
   return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

The calculations are

0 *255/1023 = 0
1 *255/1023 = 0
2 *255/1023 = 0
3 *255/1023 = 0
4 *255/1023 = 0
5 *255/1023 = 1

1022 * 255/1023 = 254
1023 * 255/1023 = 255

These match the Arduino outputs.

Looking at a simpler output

The problem becomes even more apparent.

if you map 1023 to the output range 0 to 7 the problem becomes even more apparent.

Change the code in the previous sketch redefining 'OUTPUTS' as 8.


The output then becomes:

Arduino map output distribution
Bin size: 146
Bin: 0 count 147 ***
Bin: 1 count 146
Bin: 2 count 146
Bin: 3 count 146
Bin: 4 count 146
Bin: 5 count 146
Bin: 6 count 146
Bin: 7 count 1 ***
Bin error: 0
Bin error: 7

If you tried to map an ADC input using a potentiometer to control an 8 LED bargraph the only time the last LED would light is when the input is 1023!

To fix it make the values a multiple of a power of 2 so the fraction is not suppressed.

Note: For different ranges the difference between max and min must be a power of 2.

The following program uses power of 2 values as input and shows the corrected output.

//#define NUM_SAMPLES 1024 // Must be a power of 2 (Range 0-1023)
//#define NUM_OUTPUTS 256  // Must be a power of 2 (Range 0-255)

#define NUM_SAMPLES 1024 // Must be a power of 2 (Range 0-1023)
#define NUM_OUTPUTS 8    // Must be a power of 2 (Range 0-7)

#define NUM_BINS (NUM_OUTPUTS)

int bins[NUM_BINS];
const int binsize = (NUM_SAMPLES)/(NUM_OUTPUTS);

void setup() {

  Serial.begin(9600);
  Serial.println("Arduino map output distribution");
  Serial.print("Bin size: ");
  Serial.println(binsize);

  // Initialise bins
  for (int i = 0; i < NUM_BINS; i++) bins[i]=0;

  for (int adc = 0; adc < NUM_SAMPLES; adc++) {
    int mapped = map(adc, 0, NUM_SAMPLES, 0, NUM_OUTPUTS);
    bins[mapped] +=1;
  }

  for(int i=0;i< NUM_BINS;i++) {
    Serial.print("Bin: ");
    Serial.print(i);
    Serial.print(" count ");
    Serial.print(bins[i]);

    // Show bin distribution error
    if ((bins[i] != binsize)) {
      Serial.println(" ***") ;
    } else Serial.println();

  }

  // Find wrong bin count
  for(int i=0;i< NUM_BINS;i++) {
    if (bins[i]!=binsize) {
      Serial.print("Bin error: ");
      Serial.println(i);
    }
  }
}

void loop() {
}

Output results: for NUM_SAMPLES = 1024, NUM_OUTPUTS = 8

Arduino map output distribution
Bin size: 128
Bin: 0 count 128
Bin: 1 count 128
Bin: 2 count 128
Bin: 3 count 128
Bin: 4 count 128
Bin: 5 count 128
Bin: 6 count 128
Bin: 7 count 128

Note: The first example is also corrected with an even spread of 4 input values per output.

Another example

Here's another example using random samples and outputs i.e. non-power of 2 values.

#define SAMPLES 1976
#define OUTPUTS 153

Here 1976/153 = 12.91 and each bin has 13 values in it except the last that has one. So the algorithm gets it approximately right.

First part of the serial output
Final part of the serial output
Bin: 0 count 13 ***
Bin: 1 count 13 ***
Bin: 2 count 13 ***
Bin: 3 count 13 ***
Bin: 4 count 13 ***
Bin: 5 count 13 ***
Bin: 6 count 13 ***
Bin: 7 count 13 ***
Bin: 145 count 13 ***
Bin: 146 count 13 ***
Bin: 147 count 13 ***
Bin: 148 count 13 ***
Bin: 149 count 13 ***
Bin: 150 count 13 ***
Bin: 151 count 12
Bin: 152 count 1 ***

Arduino Map Conclusion

Given it is such a pain to get working exactly right, its probably better to do one of the following.

  • Don't worry about the error - its only small if the ouput mapped value is fairly large compared to the input  - but remember the last output value will only be generated once.
  • Use a floating point calculation - but will be slower and use more memory.
  • Avoid using map()  - For instance for an ADC conversion to 0 to 255 just use the upper 8 bits of the result - use function analogReadResolution() for this.


New! Comments

Have your say about what you just read! Leave me a comment in the box below.




Privacy Policy | Contact | About Me

Site Map | Terms of Use



Visit our Facebook Page:
To Visit Click Here


Recent Articles

  1. Arduino String: How to read commands from the serial port.

    For Arduino string operations you can use Object Class Strings or C style strings but which should you use? Also find out how to decode commands and control variables in your programs using strings.

    Read more

  2. A Real Time Clock design (DS1307) with a PIC microcontroller

    Real Time Clock Design (FREE): A Free and Complete RTC design using the DS1307 and a PIC micro (16F88) also re-targetable. This PIC project uses an I2C Clock chip and 7-segment display to create a fou…

    Read more

  3. How to use the ADXL345 for movement sensing and more.

    With the ADXL345 acellerometer you can detect up to 16g! You can also find out how to use it for tap detection and more.

    Read more

  4. Arduino Interrupt : There are Some You May Never Have Heard About!

    Arduino Interrupt Tutorial: Find out how many external there are on an Arduino Uno - The answer is more than two!

    Read more

  5. Easy Switch Debounce

    Switch debounce: Three different ways to debounce input push switches with one amazing method that you can't miss.

    Read more

  6. How to use the ADS1115

    A tutorial on using the ADS1115 precision 16 bit ADC for low power use.

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top