map() not working as expected

Hi everyone,

I'm having a few issues using map in a sketch. It could well be my understanding of the way it works but I've used it before and got the exact result I was expecting.

The line I am having trouble with is (I'll post full sketch at the end of this post) is ...

int distMap = map(dist, 5, 125, 0, 24);

The variable "dist" is a distance returned from an ultrasonic sensor (SR04) and is working perfectly (and very accurately!). I am building a parking radar which takes the distance from the sensor and lights the appropriate number of lines on 3 8x8 matrix displays. The closer you get the less lines are displayed and when the final one goes out you stop. I have mapped the distance to the variable distMap from 0 - 24 so that I could easily read off how many lines on the displays I needed to light up.

I had set up if statements to evaluate the size of the distMap ...

distMap == 24 (turns on all display lines)
distMap < 24 && distMap > 16 (turns on top 2 displays and shows distMap-16 lines on last display)
distMap <= 16 && distMap > 8 (turns on top display, shows distMap-8 lines on second, and turns off last)
distMap <= 8 && dist > 5 (turns off bottom 2 displays and shows distMap lines on top display)
else (if dist is less than 5 flash all lights as an emergency stop)

When I first ran the sketch it displayed erroneous data on the display and I was getting the emergency stop signal even though I knew the sensor was about 150cm from a solid wall I was using to test. As I pushed it closer the displays then began to work correctly. I added a serial output so I could see what was going on and it showed that when dist was greater than 125 (and max value I had supplied) the map was generating figures above 24 (again the max value I had selected). The mapping still seemed to be working correctly, i.e. when I moved the sensor to 130cm I would get a reading of 25, 135cm / 26, etc, but my (maybe flawed) understanding of the way that map worked was that it also constrained the figure to the max selected.

I have obviously changed the first if statement from distMap == 24 to distMap >= 24 which now gives the intended functionality but I am still scratching my head as to why this didn't work as intended.

If someone could explain what I've done wrong, or where my understanding is lacking, I would most grateful. Also if you have any suggestions on how I could make the code more efficient that'd be great.

Many thanks!

TH

/*
Sketch to control 3 8x8 matrices in a bar graph
style using an SR04 split into functions
*/

const int dataPin = 6; // 595 pin 14
const int latchPin = 4; // 595 pin 12
const int clockPin = 2; // 595 pin 11

/* 
On 595 connect pins 8 & 13 to GND and
pins 10 & 16 to 5v. Outputs are on pins
15 & 1-7. Flowthrough is out pin 9 which
connects to pin 14 (data) on the next 595
*/

const int trigPin = 8;
const int echoPin = 10;
byte dispValue[] = {0, 1, 3, 7, 15, 31, 63, 127, 255}; // byte array of values to be displayed

void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  //Serial.begin(9600);
  
}

void loop() {
  int distance = getDist();
  updateBar(distance);
}

int getDist(){ 
  int duration, distance;
  digitalWrite(trigPin, HIGH); // sends pulse of ...
  delayMicroseconds(1000); // 1000 microseconds ...
  digitalWrite(trigPin, LOW); // and this ends the pulse
  duration = pulseIn(echoPin, HIGH); // listens for return pulse - sr04 output pulse length = time between send and receive
  distance = (duration/2) / 29.1; // converts time into distance
  return distance;
}

void updateBar(int dist){
  int distMap = map(dist, 5, 125, 0, 24); // sets the 0 distance to 10 cm as a safety margin
  //Serial.print("Distance = ");
  //Serial.print(dist);
  //Serial.print(" DistMap = ");
  //Serial.println(distMap);
  if (distMap >= 24) { // lights all displays
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);// output to 595
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(50);// prevents flicker
  }
  else if (distMap < 24 && distMap > 16) { // lights top two displays, and shows distMap-16 lines on bottom
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[distMap-16]);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(50);// prevents flicker
  }
  else if (distMap <= 16 && distMap > 8) { // lights top display, shows distMap-8 lines on middle and turns off bottom
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, 0);
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[distMap-8]);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[8]);
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(50);// prevents flicker
  }
  else if (distMap <= 8 && dist >= 5) { // turns off bottom two displays and shows distMap lines on top
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, 0);
    shiftOut(dataPin, clockPin, MSBFIRST, 0);
    shiftOut(dataPin, clockPin, MSBFIRST, dispValue[distMap]);// output to 595
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(50);// prevents flicker
  }
  else { // distance less than 5 // flashes alternate lines as emergency stop signal
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, 85);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, 85);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, 85);// output to 595
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(100);
    digitalWrite(latchPin, LOW);// arms shift reg
    shiftOut(dataPin, clockPin, MSBFIRST, 170);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, 170);// output to 595
    shiftOut(dataPin, clockPin, MSBFIRST, 170);// output to 595
    digitalWrite(latchPin, HIGH);//tells 595 to output received data
    delay(100);
  }
}

thermalhound:

int distMap = map(dist, 5, 125, 0, 24);

If someone could explain what I've done wrong, or where my understanding is lacking, I would most grateful.

I think the problem is that most people assume that the output of that map() call will always be a number from 0 to 24. That is true ONLY if the input is in the range 5 to 125. If the input is outside that range the output is likely to be outside the expected range as well. For example:

map(300, 100, 200, 0, 1000);

This will return 2000 because 300 is 100% greater than the high end of the input range and 2000 is 100% greater than the high end of the output range.

Hi John,

Thanks for that, makes perfect sense! I had a feeling it was my flawed understanding. I guess if I really needed it to only generate the maximum number (in this case 24) I could always just constrain the input value to a maximum of the maximum value (again in this case 125).

Thanks!

You could constrain the input:

int distMap = map(constrain(dist,5,125), 5, 125, 0, 24);

or constrain the output:

int distMap = constrain(map(dist, 5, 125, 0, 24),0,24);

or both, but that would be silly:

int distMap = constrain(map(constrain(dist,5,125), 5, 125, 0, 24),0,24);