Problems with Ultrasonic Range Finder - Maxbotix LV-EZ1

Hi, I am using three of the Ultrasonic Range Finder - Maxbotix LV-EZ1’s in my project. My project requires continuous readings of the distances. My problem is that I am not getting the correct readings from the sensors. I am thinking that it is either my code or the fact that I didn’t have the RX pin hooked up at the time of testing (I don’t know much about this pin). Here is my code for one of the sensors:

const int rx = 0;
pinMode(rx, INPUT);
digitalWrite(rx, HIGH);
  const int anPin = A0; 
  //MaxSonar Analog reads are known to be very sensitive. See the Arduino forum for more information.
  //A simple fix is to average out a sample of n readings to get a more consistant reading.\\ 
  //Even with averaging I still find it to be less accurate than the pw method.\\ 
  //This loop gets 60 reads and averages them

  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway1 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in1, ");
  Serial.print(cmAway1);
  Serial.print("cm1");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);

Any help would be appreciated, as I am a beginner in Arduino

const int rx = 0;
pinMode(rx, INPUT);
digitalWrite(rx, HIGH);

Pin 0 is one of the serial I/O pins. Best not to muck withy it if you aren’t going to use it.

Nothing obviously wrong in this part of the code. In what way is the data you are receiving different from what you expect?

Does your code work as expected for one sensor?

Yes, the code works fine for one sensor but does not work for all three. Again, I did not have the RX pin hooked up at the time. Is that a necessity for multiple sensors?

alexalbert2000: Yes, the code works fine for one sensor but does not work for all three. Again, I did not have the RX pin hooked up at the time. Is that a necessity for multiple sensors?

Probably not.

There may be a mistake in the code you didn't show. That's why it is usually best to show ALL of your code and to state exactly how the output differs from what you expect. " I am not getting the correct readings from the sensors." is not very helpful.

Ok, here is all the code. Forgot to note that I am also controlling 3 vibration motors at different intensities depending on the data they get from their correlating sensor.

const int anPin = A0; 

long anVolt, inches, cm;
int cmAway1, cmAway2, cmAway3, cmAway4, cmAway5;
int sum=0;//Create sum variable so it can be averaged
int avgrange=60;//Quantity of values to average (sample size)


void setup() {


  Serial.begin(9600);
}



void loop() {

  const int anPin = A0; 
  //MaxSonar Analog reads are known to be very sensitive. See the Arduino forum for more information.
  //A simple fix is to average out a sample of n readings to get a more consistant reading.\\ 
  //Even with averaging I still find it to be less accurate than the pw method.\\ 
  //This loop gets 60 reads and averages them

  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway1 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway1);
  Serial.print("cm");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);



  const int motorPin1 = 7;
  
 
   
    pinMode (motorPin1, OUTPUT);
   
    if (cmAway1 >= 200)
     {
      analogWrite(motorPin1, 0);
     }
     if(cmAway1 > 150 & cmAway1 < 199)
     {
       analogWrite(motorPin1, 64);
     }
     if(cmAway1 > 100 & cmAway1 < 149)
     {
       analogWrite(motorPin1, 127);
     }
     if(cmAway1 > 50 & cmAway1 < 99)
     {
       analogWrite(motorPin1, 191);
     }
     if(cmAway1 > 0 & cmAway1 < 49)
     {
       analogWrite(motorPin1, 255);
     }
  
  

const int an2Pin = A3; 




  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway2 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway2);
  Serial.print("cm");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);



  const int motorPin2 = 3;
  
 
   
    pinMode (motorPin2, OUTPUT);
   
    if (cmAway2 >= 200)
     {
      analogWrite(motorPin2, 0);
     }
     if(cmAway2 > 150 & cmAway2 < 199)
     {
       analogWrite(motorPin2, 64);
     }
     if(cmAway2 > 100 & cmAway2 < 149)
     {
       analogWrite(motorPin2, 127);
     }
     if(cmAway2 > 50 & cmAway2 < 99)
     {
       analogWrite(motorPin2, 191);
     }
     if(cmAway2 > 0 & cmAway2 < 9)
     {
       analogWrite(motorPin2, 255);
     }


const int an3Pin = A5; 


  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway3 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway3);
  Serial.print("cm");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);



  const int motorPin3 = 5;
  
 
    pinMode (motorPin3, OUTPUT);
   
    if (cmAway3 >= 200)
     {
      analogWrite(motorPin3, 0);
     }
     if(cmAway3 > 150 & cmAway3 < 199)
     {
       analogWrite(motorPin3, 64);
     }
     if(cmAway3 > 100 & cmAway3 < 149)
     {
       analogWrite(motorPin3, 127);
     }
     if(cmAway3 > 50 & cmAway3 < 99)
     {
       analogWrite(motorPin3, 191);
     }
     if(cmAway3 > 0 & cmAway3 < 49)
     {
       analogWrite(motorPin3, 255);
     }
  
  

const int an4Pin = A3; 

 
  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway4 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway4);
  Serial.print("cm");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);


  const int motorPin4 = 9;
 
  
    pinMode (motorPin4, OUTPUT);
   
    if (cmAway4 >= 200)
     {
      analogWrite(motorPin4, 0);
     }
     if(cmAway4 > 150 & cmAway4 < 199)
     {
       analogWrite(motorPin4, 64);
     }
     if(cmAway4 > 100 & cmAway4 < 149)
     {
       analogWrite(motorPin4, 127);
     }
     if(cmAway4 > 50 & cmAway4 < 99)
     {
       analogWrite(motorPin4, 191);
     }
     if(cmAway4 > 0 & cmAway4 < 49)
     {
       analogWrite(motorPin4, 255);
     }

    

const int an5Pin = A4; 


  for(int i = 0; i < avgrange ; i++)
  {
    //Used to read in the analog voltage output that is being sent by the MaxSonar device.
    //Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    //Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    anVolt = analogRead(anPin)/2;
    sum += anVolt;
    delay(10);
  }  
  inches = sum/avgrange;
  cmAway5 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway5);
  Serial.print("cm");
  Serial.println();
  //reset sample total
  sum = 0;
  delay(500);

  const int motorPin5 = 10;
 
    pinMode (motorPin5, OUTPUT);
   
    if (cmAway5 >= 200)
     {
      analogWrite(motorPin5, 0);
     }
     if(cmAway5 > 150 & cmAway5 < 199)
     {
       analogWrite(motorPin5, 64);
     }
     if(cmAway5 > 100 & cmAway5 < 149)
     {
       analogWrite(motorPin5, 127);
     }
     if(cmAway5 > 50 & cmAway5 < 99)
     {
       analogWrite(motorPin5, 191);
     }
     if(cmAway5 > 0 & cmAway5 < 49)
     {
       analogWrite(motorPin5, 255);
     }
}

What I meant by “not getting the correct readings” was that the serial monitor usually displayed a number too high than the actual distance. Sorry for the confusion.

It is good practice to put all your constants at the top of the file (as shown below). That way you would probably have noticed that you used A3 for both an2Pin and an4Pin.

If you are using an Arduino UNO you should note that analogWrite() only works on pins 3, 5, 6, 9, 10, and 11… You are trying to use Pin 7 for analogWrite().

Since the pinMode() doesn’t need to be re-set if it doesn’t change it is traditional to put the pinMode() calls in the setup() function (as shown below).

If you find yourself writing nearly the exact same block of code more than once it is good practice to put it in a function (as shown below). This makes it easier to make changes without having to repeat those changes.

const int anPin = A0; 
const int an2Pin = A3;
const int an3Pin = A5; 
const int an4Pin = A3; 
const int an5Pin = A4; 
const int motorPin1 = 7;
const int motorPin2 = 3;
const int motorPin3 = 5;
const int motorPin4 = 9;
const int motorPin5 = 10;
const int AvgRange=60; //Quantity of values to average (sample size)

void setup() {
  Serial.begin(9600);

  pinMode (motorPin1, OUTPUT);
  pinMode (motorPin2, OUTPUT);
  pinMode (motorPin3, OUTPUT);
  pinMode (motorPin4, OUTPUT);
  pinMode (motorPin5, OUTPUT);
}

void loop() {
  ActOnInput(anPin, motorPin1);
  ActOnInput(an2Pin, motorPin2);
  ActOnInput(an3Pin, motorPin3);
  ActOnInput(an4Pin, motorPin4);
  ActOnInput(an5Pin, motorPin5);
}

void ActOnInput( unsigned char analogPin, unsigned char motorPin) {

  //MaxSonar Analog reads are known to be very sensitive. See the Arduino forum for more information.
  //A simple fix is to average out a sample of n readings to get a more consistant reading.\\ 
  //Even with averaging I still find it to be less accurate than the pw method.\\ 
  //This loop gets 60 reads and averages them
  //reset sample total
  unsigned long inches = 0;
  for(int i = 0; i < AvgRange ; i++)
  {
    // Used to read in the analog voltage output that is being sent by the MaxSonar device.
    // Scale factor is (Vcc/512) per inch. A 5V supply yields ~9.8mV/in
    // Arduino analog pin goes from 0 to 1024, so the value has to be divided by 2 to get the actual inches
    inches += analogRead(anPin)/2;
    delay(10);
  }  
  inches = inches/AvgRange;
  int cmAway1 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway1);
  Serial.println("cm");

  delay(500);

  if (cmAway1 >= 200)
  {
    analogWrite(motorPin, 0);
  }
  if(cmAway1 > 150 & cmAway1 < 199)
  {
    analogWrite(motorPin, 64);
  }
  if(cmAway1 > 100 & cmAway1 < 149)
  {
    analogWrite(motorPin, 127);
  }
  if(cmAway1 > 50 & cmAway1 < 99)
  {
    analogWrite(motorPin, 191);
  }
  if(cmAway1 > 0 & cmAway1 < 49)
  {
    analogWrite(motorPin, 255);
  }
}

Ok, I finally got my project to work!! However, there are a few things I would like to improve on that I was wondering if you could help me with. First, the feedback I am getting from the motors comes in 20-30 second intervals. I would like to make this more continuous (does this happen because of the average filter?). Secondly, only one motor vibrates at a time. Is there a way for more than one motor to vibrate in unison? Thank you so much though for helping me with the code so far! :)

Because of delays you put in it takes at least 1.1 seconds for each analog input (60 * 0.01 + 500). I would take out the delays and reduce the number of samples being averaged to 10 to see if that makes the code responsive enough.

I did that and I greatly increased the speed but now the readings are all messed up. For example, when looking at the distances in the serial monitor, there would be times when the inches was at 192, but there was 0 cm. I don't know what's happened but I can't get it back to normal.

alexalbert2000:
I did that and I greatly increased the speed but now the readings are all messed up. For example, when looking at the distances in the serial monitor, there would be times when the inches was at 192, but there was 0 cm.

  inches = inches/AvgRange;
  int cmAway1 = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cmAway1);
  Serial.println("cm");

It shouldn’t be possible to get 192 inches and 0 cm at the same time.

There was a message from maxbotix at one point that said you should never, ever, under any circumstances take the average of multiple readings. You should always use a median or mode. I tripped over this same issue when using a maxbotic sensor since I occasionally got readings that were way off. These readings screw up the average but are ignored when using median or mode.

I fixed the issue of getting inches and centimeter readings that don’t equal, but I am still getting readings that are incorrect (usually way too high of a number). On average, I get a correct reading one out of every five times. Again, would a mode/median filter help this?

Ok, I have just discovered something. Every fourth reading, is significantly lower than the other readings. For example:

2in, 5cm1
185in, 469cm2
194in, 492cm3
184in, 467cm1
12in, 30cm2
149in, 378cm3
158in, 401cm1
162in, 411cm2
9in, 22cm3
166in, 421cm1
175in, 444cm2
175in, 444cm3
2in, 5cm1
184in, 467cm2
193in, 490cm3

What would be causing this to happen?

alexalbert2000: Ok, I have just discovered something. Every fourth reading, is significantly lower than the other readings. For example:

What would be causing this to happen?

Very strange result. With multiple sensors I would worry about a sensor receiving an echo from another sensor. Are you sure the sensitivity patterns don't overlap at all?

Well, that is why I wanted to use the RX. Maxbotix said to use it in this article: http://www.maxbotix.com/documents/LV_Chaining_Simultaneous_Operation_AN_Out.pdf. The only problem is that I don't know how to pull the RX pin high for more than 20uS but less than 400uS. Do you know how to do this?

alexalbert2000: Well, that is why I wanted to use the RX. Maxbotix said to use it in this article: http://www.maxbotix.com/documents/LV_Chaining_Simultaneous_Operation_AN_Out.pdf. The only problem is that I don't know how to pull the RX pin high for more than 20uS but less than 400uS. Do you know how to do this?

To be really sure you should use direct port addressing:

// Assume that the RX pin is connected to Pin 5 which is PORTD bit 5
// https://spreadsheets.google.com/spreadsheet/pub?key=0AtfNMvfWhA_ccnRId19SNmVWTDE0MEtTOV9HOEdQa0E&gid=0

// Fast equivalent of digitalWrite(5, HIGH); should take less than a microsecond
PORTD |= B00100000;

delayMicroseconds(20);

// Fast equivalent of digitalWrite(5, LOW); should take less than a microsecond
PORTD &= B11011111;

I am sorry :~ I am still learning Arduino programming and I did not get what the code meant in the last reply. Is there a way you could simplify what direct port addressing is and how to use the code you wrote?

alexalbert2000:
I am sorry :~ I am still learning Arduino programming and I did not get what the code meant in the last reply. Is there a way you could simplify what direct port addressing is and how to use the code you wrote?

Every I/O pin is represented by a bit in an 8-bit I/O port. This spreadsheet:

shows the mapping of pin number to port and bit for the UNO and MEGA. By turning the bit on (HiGH) and off (LOW) directly you can avoid much of the overhead of the digitalWrite() function.

See: http://arduino.cc/en/Reference/PortManipulation

Every I/O pin is represented by a bit in an 8-bit I/O port. This spreadsheet: https://spreadsheets.google.com/spreadsheet/pub?key=0AtfNMvfWhA_ccnRId19SNmVWTDE0MEtTOV9HOEdQa0E&gid=0 shows the mapping of pin number to port and bit for the UNO and MEGA. By turning the bit on (HiGH) and off (LOW) directly you can avoid much of the overhead of the digitalWrite() function.

Ok, I have better understanding of why to use direct port addressing, but I am still confused on how to implement the pin I want to use for RX in the code you wrote for the example pin 5. Could I use pin 11 (since it is one of my empty pins) instead of pin 0?

alexalbert2000: I am still confused on how to implement the pin I want to use for RX in the code you wrote for the example pin 5. Could I use pin 11 (since it is one of my empty pins) instead of pin 0?

Yes. If you are using an UNO or MEGA just look up the pin number in the spreadsheet I pointed to. That will tell you which port and which pin. Bits are numbered from the right. Bit 0 is the rightmost bit. Bit 7 is the leftmost bit. 1 == HIGH and 0 == LOW.