Distance Detection

with MaxSonar ultrasonic rangefinder

Library

A library is available at https://github.com/Diaoul/arduino-Maxbotix

It features:

  • Inputs: TX, PW and AN
  • Models: LV, XL and HRLV
  • Filters: NONE, MEDIAN, HIGHEST_MODE, LOWEST_MODE, SIMPLE, BEST

I recommend looking at the examples to get started with it.

Manually

This is a writeup on the MaxSonar ultrasonic range finder. I felt there was a lack of information out there on this product so I thought this might help out people who are learning.

First of all, I used the LV-MaxSonar EZ1 but I imagine the fundamentals will be the same and can be applied to all the MaxSonar products.

Please feel free to update the information here if you find different results for different products.


There are 3 ways to interface the MaxSonar.

  1. Analog. Very simple, but not as accurate as PW in my experience (Bruce Allen).
  2. PW - Pulse Width. I found there was not much information out there but it is easy to do (Bruce Allen and Jason Lessels).
  3. Serial Communication (Daniel Matthews).


Analog

 

//Feel free to use this code.
//Please be respectful by acknowledging the author in the code if you use or modify it.
//Author: Bruce Allen
//Date: 23/07/09

//Analog pin 1 for reading in the analog voltage from the MaxSonar device.
//This variable is a constant because the pin will not change throughout execution of this code.
const int anPin = 1;

//variables needed to store values
long anVolt, inches, cm;
int sum = 0; //Create sum variable so it can be averaged
int avgrange = 60; //Quantity of values to average (sample size)

void setup()
{
  //This opens up a serial connection to shoot the results back to the PC console
  Serial.begin(9600);
}

void loop()
{
  //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 consistent 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;
  cm = inches * 2.54;
  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();

  //reset sample total
  sum = 0;

  delay(500);
}

PW

PW without mode filter

I found this to be more accurate than the analog method and just as easy to implement.

 

//Feel free to use this code.
//Please be respectful by acknowledging the author in the code if you use or modify it.
//Author: Bruce Allen
//Date: 23/07/09

//Digital pin 7 for reading in the pulse width from the MaxSonar device.
//This variable is a constant because the pin will not change throughout execution of this code.
const int pwPin = 7;

//variables needed to store values
long pulse, inches, cm;

void setup()
{
  //This opens up a serial connection to shoot the results back to the PC console
  Serial.begin(9600);
}

void loop()
{
  pinMode(pwPin, INPUT);

  //Used to read in the pulse that is being sent by the MaxSonar device.
  //Pulse Width representation with a scale factor of 147 uS per Inch.

  pulse = pulseIn(pwPin, HIGH);
  //147uS per inch
  inches = pulse / 147;
  //change inches to centimetres
  cm = inches * 2.54;

  Serial.print(inches);
  Serial.print("in, ");
  Serial.print(cm);
  Serial.print("cm");
  Serial.println();

  delay(500);
}

PW with mode filter

Based on the above examples and the median filter provided below, I made a mode filter for too be used with the sonar. I used this in conjunction with pulse width, but I'm sure it would work with the analog readings just as well.

 

/*
  This script is designed to take several readings from the MaxBotix sonar and generate a mode/median.
  Author: Jason Lessels
  Date created: 2011/June/06
  License: GPL (=>2)
  This work has been compiled using many sources mainly posts/wiki posts from;
  Allen, Bruce (2009/July/23) and Gentles, Bill (2010/Nov/12)
*/


//Set the pin to receive the signal.
const int pwPin = 7;
//variables needed to store values
int arraysize = 9;  //quantity of values to find the median (sample size). Needs to be an odd number

//declare an array to store the samples. not necessary to zero the array values here, it just makes the code clearer
int rangevalue[] = {  0, 0, 0, 0, 0, 0, 0, 0, 0};
long pulse;
int modE;

void setup()
{
  //Open up a serial connection
  Serial.begin(9600);
  //Wait for the serial connection
  delay(500);
}

//Main loop where the action takes place
void loop()
{
  pinMode(pwPin, INPUT);

  for (int i = 0; i < arraysize; i++)
  {

    pulse = pulseIn(pwPin, HIGH);
    rangevalue[i] = pulse / 58;
    delay(10);
  }

  Serial.print("Unsorted: ");
  printArray(rangevalue, arraysize);
  isort(rangevalue, arraysize);
  Serial.print("Sorted: ");
  printArray(rangevalue, arraysize);
  modE = mode(rangevalue, arraysize);
  Serial.print("The mode/median is: ");
  Serial.print(modE);
  Serial.println();
  delay(1000);
}


/*-----------Functions------------*/
//Function to print the arrays.
void printArray(int *a, int n)
{
  for (int i = 0; i < n; i++)
  {
    Serial.print(a[i], DEC);
    Serial.print(' ');
  }

  Serial.println();
}

//Sorting function
// sort function (Author: Bill Gentles, Nov. 12, 2010)
void isort(int *a, int n) {
  //  *a is an array pointer function

  for (int i = 1; i < n; ++i)
  {
    int j = a[i];
    int k;
    for (k = i - 1; (k >= 0) && (j < a[k]); k--)
    {
      a[k + 1] = a[k];
    }
    a[k + 1] = j;
  }
}

//Mode function, returning the mode or median.
int mode(int *x, int n) {
  int i = 0;
  int count = 0;
  int maxCount = 0;
  int mode = 0;
  int bimodal;
  int prevCount = 0;

  while (i < (n - 1)) {
    prevCount = count;
    count = 0;

    while (x[i] == x[i + 1]) {
      count++;
      i++;
    }

    if (count > prevCount & count > maxCount) {
      mode = x[i];
      maxCount = count;
      bimodal = 0;
    }
    if (count == 0) {
      i++;
    }
    if (count == maxCount) { //If the dataset has 2 or more modes.
      bimodal = 1;
    }
    if (mode == 0 || bimodal == 1) { //Return the median if there is no mode.
      mode = x[(n / 2)];
    }
    return mode;
  }
}

Serial Communication (RS232)

After using the above examples, I felt the need to contribute to this page. Please feel free to use this sketch for serial communication between your Ardunio and LV-MaxSonar Note: the LV-MaxSonar's Rx and Tx pins (RS232 interface) are directly connected to the Arduino's digital inputs (no inversion used i.e RS232 logic).


/*
   MaxSonar Serial
   When uploaded to an Arduino, this sketch allows an Arduino to read multiple readings
   from a MaxSonar and print these reading to the Arduino IDE serial monitor.
   The Arduino powers the MaxSonar and reads data from MaxSonar's Tx pin (simplex); the
   interface between the sensor and Arduino is RS232 at TTL voltage levels
   The protocol uses one start bit and one end bit for every data-byte(no
   error detection). The Maxsonar's rx is used for software flow control.
   The data-bytes are coded using ASCII format where each reading is precedded by the 'R'
   character and ended by the return carriage.
   The arduino prints the each reading to the serial monitor.
   Author: Daniel Matthews
   Date: January 2018
*/

//the pin connected to MaxSonar's tx pin
#define rx 6
//the pin connected to MaxSonar's rx pin
#define tx 7
//the bit duration and the bit half duration at 9600bits/second
#define bit9600Delay 104
#define halfBit9600Delay 52
//the number of bytes in a data packet
#define numberOfBytes 5
/* function declarations*/
//function to read a packet(reading) from the sensor
boolean readSonar(char []);
//function to read a byte from the sensor
void readSonarByte(char *);
//function to print reading to the serial monitor
void printSonarMessage(char []);
//function to print data from sensor to the serial monitor (to troubleshoot)
void printSonarData(char []);

void setup()
{
  // initalise the serial communication (USB) at 9600bits per second
  Serial.begin(9600);
  // define the rx pin to be an input in and the tx line pin to be an output pin
  pinMode(rx, INPUT);
  pinMode(tx, OUTPUT);
  //start ranging by pulling the tx line high
  digitalWrite(tx, HIGH);
}

void loop()
{
  // put your main code here, to run repeatedly:
  // packet(string) from the sensor
  char range[numberOfBytes];
  // boolean to check if the data is complete (i.e first byte is 'R')  
  boolean invalidReading;
  //read data from the sensor
  invalidReading = readSonar(range);
  //if the data is complete, print reading to the serial monitor
  if(!invalidReading)
  {
    //comment out the below line to see data from sensor in different formats!
    //printSonarData(range);
    //print data to the serial monitor
    printSonarMessage(range);
  }
  //delay between taking readings from the sensor
  delay(100);
}

boolean readSonar(char range[])
{
  //counting variable (for character index)
  int i = 0;
  //boolean to store
  boolean invalid = false;
  //read a byte of data from the rx line
  readSonarByte(&range[0]);
  //if this byte is the first, keep reading bytes from the rx line
  if (range[0] == 'R')
  {
    //keep reading until the last byte (in the message) is read
    while(i < numberOfBytes)
    {
    readSonarByte(&range[++i]);
    }        
  }
  else
  {
  //message is incomplete as start has not been read
  invalid = true;  
  }
return invalid;
}

void readSonarByte(char *value)
{
   //wait until the end of the start bit
   *value = 0;
   while(digitalRead(rx) == 0);
   //wait until the end of the start bit
   if(digitalRead(rx) == HIGH)
   {
   //wait half a bit to improve sampling
   delayMicroseconds(halfBit9600Delay);
   for (int bitShift = 0; bitShift < 7; bitShift++)
   {
    //wait for next bit
    delayMicroseconds(bit9600Delay);
    //read bit of data and store in the character
    //by reading rx line, inverting that signal,
    //binary shifting the signal to correct position
    //and bit-wise 'oring' that mask with the current character
    *value |= ((!digitalRead(rx)) << bitShift);
   }
   //wait for end bit
   delayMicroseconds(bit9600Delay);
   }  
}

void printSonarMessage(char range[])
{
    //replace the return carriage with null
    //so that character array is a printable string
    range[numberOfBytes-1] = 0;
    //print string (reading) to the serial monitor
    Serial.println(range);
}
void printSonarData(char range[])
{
//print the data in differnt formats
for(int i = 0; i < numberOfBytes; i++)  
{
      //print the byte (element) number in the character array  
      Serial.print(i);
      Serial.print("\t");
      //print the byte in ASCII format
      Serial.print(range[i]);
      Serial.print("\t");
      //print the byte in hexidecimal
      Serial.print(range[i], HEX);
      Serial.print("\t");
      //print the byte in decimal
      Serial.print(range[i], DEC);
      Serial.print("\t");
      //print the byte in binary
      Serial.println(range[i], BIN);
}
}
 

Notice

Please check this page before using LV-MaxSonar:

http://forum.arduino.cc/index.php/topic,20920.html

You can find a very useful post by Scott from MaxBotix Inc.

Take care with:

enjoy!

karduino:D

Share