multiple maxbotix LVEZ on one UNO?

Hi all,

Apologies as this is a little long and I am quite new to this -- I followed this tutorial (electrojoystick.com) to get a Maxbotix LV-EZ1 sensor sending data via analog output to an Uno board connected via USB to a Macbook Pro. I get accurate data into the serial monitor and can read it in Max/MSP (final goal) -- this part is working!

I would like to know how many of these sensors I can use with one Uno board. Right now I have two connected in parallel but only one is reading with any accuracy; the other is printing to the serial bus correctly, but always 6 inches. Both are powered by the single 5v pin, each connected to separate GND pins and into analog 0 and 1 respectively. I have been trying to get values from each in alternation by repeating the code from the tutorial with each analog pin:

int configPin = 13; //Set the sonar Calibration Pin

void setup() { //begin of program

Serial.begin(9600); //serial baud rate 9600

pinMode(configPin,OUTPUT); //make Calibration pin output

}

void loop(){ //looping of program

digitalWrite(configPin,HIGH); //raise the reset pin high

delay(200); //start of 1st calibration ring

float sensorvalue1 = analogRead(0); //get analog sensor value from pin 0

float inchvalue1 = (254.0/1024.0) 2.0 sensorvalue1; //convert to inches

Serial.print("01 "); //print inch text

Serial.println(inchvalue1); //print inch value

delay(100); //optional delay 100 ms

digitalWrite(configPin,LOW); //turn off 1st Calibration ring and sensor

delay(200); //delay between cycles

digitalWrite(configPin,HIGH); //raise the reset pin high

delay(200); //start of 2nd calibration ring

float sensorvalue2 = analogRead(1); //get analog sensor value from pin 1

float inchvalue2 = (254.0/1024.0) 2.0 sensorvalue2; //convert to inches

Serial.print("02 "); //print inch text

Serial.println(inchvalue2); //print inch value

delay(100); //optional delay 100 ms

digitalWrite(configPin,LOW); //turn off 2nd Calibration ring and sensor

delay(100); //delay 100 ms

}

Is the code wrong, or do I have to change how things are connected? Do I need all of this code toggling the configuration pin? Do I need to establish separate configPin for each analog pin I'm using? I have tried without it and it seems to make the readings more accurate, but I don't understand how it works exactly. For the wires coming out of the same pin on the Uno, I just have the exposed copper wrapped together, no resistors or anything like that.

If I am posting in the wrong place please re-direct me.

Thanks!

I recently got two maxbotix LV-EZ4 sensors, and have them connected in what maxbotix calls the Constantly Looping config (see application notes on this page: Documents & Downloads – MaxBotix). In this config, each sensor needs +V, Gnd, and an analog line to read it, as well as one pin to act as the initial kick-off control. For one sensor, this is four pins, and you'll need an additional analog pin for each sensor connected. Check out maxbotix's app notes if you haven't already -- they have great diagrams and instructions.

I skimmed your code, and got lost with the delays a bit. What are you calling the calibration pin? Is that the pin connected to the sensor's RX line? Where is your EN pin connected? Mine is connected to Vcc permanently.

Here's some code I have. From my setup() function I call sonarInit(SONAR_CTL) that boots the sensors (configured as above). This takes about half a second for continuous ranging to begin.

#define Vcc           5.0f
#define SONAR_CTL     A1
#define SONAR_FIRST   A2
#define SONAR_SECOND  A3

void sonarInit(int controlPin)
{
  // wait for boot
  delay(250);
  // trigger ranging
  digitalWrite(controlPin, HIGH);
  delayMicroseconds(25);
  digitalWrite(controlPin, LOW);
  // Make high-impedance input to signal round-robin ranging.
  pinMode(controlPin, INPUT);
  // wait for calibration+initial ranging times.
  delay(49+49+100);
  // sonars should alternate automatically now
}

void setup()
{
  Serial.begin(38400);
  Serial.println("Sonar test (EZ4) Start");

  // Sonar setup
  pinMode(SONAR_CTL, OUTPUT);
  pinMode(SONAR_FIRST, INPUT);
  pinMode(SONAR_SECOND, INPUT);

  Serial.println("boot");
  sonarInit(SONAR_CTL);
  Serial.println("first");
  sonarRange(SONAR_FIRST);

  Serial.println("running");
}

Once it's all initialized, my loop() function just calls the sonarRange() on each analog input:

// No need to call faster than 49msec per sensor
int sonarRange(int sonarPin)
{
  // At 5V, inches = analogRead() / 1024 * Vcc / 0.009 = 0.542534722
  return analogRead(sonarPin) * 0.542534722f;
}

void loop()
{
  int leftDist = sonarRange(SONAR_FIRST);
  int rightDist = sonarRange(SONAR_SECOND);

  Serial.print("L = "); Serial.print(leftDist);
  Serial.print("  R = "); Serial.println(rightDist);
  delay(333); // Must be at least 50
}

I hope this was helpful. This code works great for the sensors I have. The caveat to having sensors auto-range round-robin like this is that each one takes 49ms, so your sensor frequency goes from 20Hz, to 10Hz, to 6Hz, to 5Hz, etc. as you add sensors. A 10Hz frequency is fine for my project.

@RoverHacker

Hey there,
Am having success using a single LV1 with PWM, but now I would like to try using two LV1's. It seems the only way to do this is to use the analog chaining. I'm a little confused about which pin on the LV1 you are referring to when you say control pin. Is that the RX pin? And what is the EN pin? Maxbotix also refers to the BW pins on all the LV's in the chain going to logic high, so should I just wire that to the 5v rail?

I believe you cannot use the same pin to trigger both sonars, as they will ping simultaneously
and the waves will interfere. You also cannot let them free-run, as they will again interfere.

And if you use separate trigger pins then you might as well use the pulsewidth mode, rather
than the analog mode, as things will go faster. Most of the time, you will get a pulse out in
maybe 10-20 msec. With analog mode, as I think you know, you have to wait 50-msec for the
analog signals to stabilize.

Oric Dan,
So are you saying that with both sensors in PWM mode, I could wire each sensor with RX to a digital pin to turn it on and just add a delay in the code so that they fire at different times? That way they they wouldn't read each other's ping. The code would be much simpler I think.

Oric Dan,
So are you saying that with both sensors in PWM mode, I could wire each sensor with RX to a digital pin to turn it on and just add a delay in the code so that they fire at different times? That way they they wouldn't read each other's ping. The code would be much simpler I think.

RX is the trigger-in pin, and PW is the pulsewidth-out pin. You first trigger and read one sonar, and
then trigger and read the other. There is no overlap in pings. You actually don't need a delay in the
code, as you don't trigger the 2nd until the PW signal of the first has been read.

I believe there is a sketch on the Playground or somewheres for the Maxsonars that uses pulsein()
to read PW. Whenever I get to it, I plan to write a routine for my ServoTank that uses external
interrupts to read the PW pulses, so I don't have to wait on pulsein().

@TeslaIaint:
@Oric:

My mistake, I mentioned the EN pin, but I meant BW pin.

When the sensors are chained together (read the app note I referenced, also described below), You only need to use one control pin on the arduino to drive however many sensors you have chained together. Each sensor will take 49msec to do a ranging, but the real nice thing about these sensors, especially analog output, is that you can read the result at any time, and it will be the most recent measurement. So in my code, I tickle the sensor array ONCE, then just read the analog pins no more frequently than 50msec (or whatever).

For chaining, you connect the arduino's control pin (whichever one you pick) to the first sensor's RX pin. Then connect the first sensor's TX pin to the second sensor's RX pin. Repeat this for however many sensors you have. Then the last sensor's TX pin is looped back to the first sensor's RX pin through a 1K resistor. On each sensor, the BW is indeed tied to +5V.

The downside to chaining N resistors is that the frequency you can check each sensor goes down. One sensor is 1/49msec, two is 1/98msec, ... 1/(N*49msec). Or roughly, 20Hz, 10Hz, 7Hz, 5Hz, etc.

@Oric:

You can certainly trigger two sensors with the same arduino control pin. BUT, it depends on how the sensors are arranged on your gadget, and in what environment they are used. I'm building a 4WD rover for long-distance outdoor use, and I could conceivably trigger sensor arrays on the front and rear simultaneously. If this was used indoors, no way.

I've only been using the analog signalling because that's so easy. However, if I were to use PWM output, then I'd most likely run that through an interruptable pin so my main code doesn't have to block waiting for the signal.

@frmless_field:
Ok, you threw me off with your first post, saying "Right now I have two connected in parallel but
only one is reading with any accuracy ...". Wired in parallel is not the same as daisy-chaining.

I don't know if you've solved your problem by now, but looking at one of the appnotes on the
page mentioned, it says "Then just strobe the first sensor's RX pin and all of the sensors will read
the range in sequence".

In your original code, it looks like you're applying a very long pulse to the first RX pin, about
300-msec, whereas I think the RX pulse is supposed to be more like 20-usec.

/*
/*
Two Maxbotix LV EZ1 sonic sensors wired in parallel
 
 */
const int sonarSensorPinL = 5;   //connected to left sonar pwm pin
const int sonarSensorPinR = 6;    //connected to right sonar pwm pin
const int sonarTriggerPinR = 7;   //connected to right sonar RX pin
const int sonarTriggerPinL = 8;   //connected to left sonar RX pin
long valueL = 0;
long valueR = 0;
int inchesL = 0;
int inchesR = 0;
unsigned int calval = 0;                   


void setup()
{
  Serial.begin(9600);
  pinMode(sonarTriggerPinL, OUTPUT);
  pinMode(sonarTriggerPinR, OUTPUT);
}

void loop()
{
  if (calval < 1)  {        // this calls the calibrate subroutine only once when the sketch begins
    calibrate ();            
  }
  calval = calval + 1;   
  // Read the value from each sensor pin
  digitalWrite(sonarTriggerPinR, HIGH);
  valueR = pulseIn(sonarSensorPinR, HIGH);
  inchesR = valueR / 147;     // 147 microeconds per inch
  Serial.print("inches right = ");
  Serial.println(inchesR);
  delayMicroseconds(20);                // can be any langth of delay as long as it's >= 20 microseconds according to data sheet
  digitalWrite(sonarTriggerPinR, LOW);  //turns off right sonic sensor so that it doesn't read from left sonic pulse
  delay(250);                                         //  delay for serial monitor

  digitalWrite(sonarTriggerPinL, HIGH);
  valueL = pulseIn(sonarSensorPinL, HIGH);
  inchesL = valueL / 147;     // 147 microeconds per inch
  Serial.print("inches left = ");
  Serial.println(inchesL);
  delayMicroseconds(20);               // can be any langth of delay as long as it's >= 20 microseconds according to data sheet                 
  digitalWrite(sonarTriggerPinL, LOW);  //turns off left sonic sensor so that it doesn't read from right sonic pulse
  delay(250);                                        // delay for serial monitor
}

void calibrate ()                     
{
  digitalWrite(sonarTriggerPinR, HIGH);
  valueR = pulseIn(sonarSensorPinR, HIGH);
  delay(250);
  digitalWrite(sonarTriggerPinL, HIGH);
  valueL = pulseIn(sonarSensorPinL, HIGH);
  delay(250);

}

This is my attempt at making two LV EZ1 sensors give reliable readings without interfering with each other. They are wired in parallel. For the inches to be more readable on the serial monitor, a longer delay would be better, but I wanted some feedback on this setup. In my final design, the serial monitor will be eliminated anyway. Also, I threw in a calibration subroutine. According to the data sheet, http://www.maxbotix.com/documents/MB1010_Datasheet.pdf, the sensor will calibrate once during start up, but I'm not sure that it would calibrate properly since I'm using the RX pins to trigger the sensor. That might be a question for Maxbotix tech support. Anyway, it can't hurt, and it only happens once per 65,536 loops before it rolls back to zero and re-calibrates (see unsigned int). For my purposes that would be just fine.
Unfortunately, the only sonic sensor that I have is tied up with another project, but I have ordered two more. I won't be able to test this code until I receive them. The two sensors will be hooked up facing the same direction with about about 30 to 45 degrees offset. Any input is greatly appreciated. edit: tested and working

@teslaint

Any input is greatly appreciated.

That's basically the code I would expect to see for 2 sonars wired in parallel, except I
believe the trigger pulses are supposed to be only 20-MICROsec long or so. Yours are between
20 and 50 MILLIsec.

Thanks oric_dan,
Good point. I edited the code above. Can anybody foresee any other difficulties I might run into with this code?

Good point. I edited the code above. Can anybody foresee any other difficulties I might run into with this code?

You will make many changes ONCE you actually try running it.

Also, looking back at the code posted in the first msg of this thread by the OP, he does the same thing,
and which I imagine was a major problem. On reading the datasheet, if you keeping holding the trigger pin
RX high, then the sonar will keep pinging continuously, which is exactly the wrong thing to do if the
first trigger is supposed to set off the daisy chain.

I will wire the two sensors separately with separate RX trigger pins. When I said "parallel", I meant parallel in respect to the + and - voltage rails. I have the two sonic sensors now, and if I get a chance tonight I'll wire them up and test the code. I hope it works. I'm getting frustrated with my little project haha. I also sent an email to Maxbotix asking them about calibration.

I will wire the two sensors separately with separate RX trigger pins. When I said "parallel", I meant parallel in respect to the + and - voltage rails. I have the two sonic sensors now, and if I get a chance tonight I'll wire them up and test the code. I hope it works. I'm getting frustrated with my little project haha. I also sent an email to Maxbotix asking them about calibration.

Yours are also in parallel ... with respect to the signaling pins ... but in the 1st post, the OP said his were
also in parallel, but he actually has them daisy-chained.

Tell me how the measurement accuracy goes. My Maxsonars actually read about 2" too short on the
measurements, and I haven't figured out why as yet. I did pay attention to the business about having
an unobstructed view during powerup calibration. That idea sounds nice, but sort of a problem in
practice.

The above code posted my me is tested and working. Also, oric_dan is right about the measurements being off a bit, but my tests weren't exactly scientific. Maybe If centimeters are used instead of inches, it might be closer. This mode filter, A mode filter for the maxbotix sensor | My Arduino Project, also helps with the random distances that are obviously wrong.

Thanks to Scott Wielenberg's timely reply to my email enquiry to Maxbotix tech support, there is no need for the calibration subroutine when the RX pin is used. Scott said, "On the first commanded range reading after power-up, the sensor will calibrate."

Here is code for two Maxbotix sensors with a mode filter. It seems a little slow, and even with the mode filter the sensors read 2" to 4" short. Any input is appreciated.

/* Two LV Maxsonar EZ1 sensors with mode filter by Dan Shields 
   Adapted from Jason Lessels, Bruce Allen, and Bill Gentles.
*/
int button = 7;              
int PIRleft = A3;                                              
int LEDleft = 12;	                                          
int PIRright = A4;                                              
int LEDright = 8;
int motorL = 9;     //forward left
int motorR = 10;    //forward right
int SonarR = 5;
int TriggerR = 2;
int SonarL = 3;
int TriggerL = 4;
int val;                        // variable for reading the pin status
int val2;                       // variable for reading the delayed status
int buttonState = 0;                // variable to hold the button state
int robotMode = 0; 

int arraysizeR = 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 rangevalueR[] = { 
  0, 0, 0, 0, 0, 0, 0, 0, 0};
long pulseR;
int modER;
long valueR = 0;
int arraysizeL = 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 rangevalueL[] = { 
  0, 0, 0, 0, 0, 0, 0, 0, 0};
long pulseL;
int modEL;
long valueL = 0;


void setup() {
  pinMode(button, INPUT);
  pinMode(LEDright, OUTPUT);
  pinMode(LEDleft, OUTPUT);
  pinMode(TriggerL, OUTPUT);
  pinMode(TriggerR, OUTPUT);
  pinMode(motorR, OUTPUT);
  pinMode(motorL, OUTPUT);
  Serial.begin(9600);
}

void loop()  {

 distCalcL();
  distCalcR();
  if (modEL >= 14 && modEL <= 36) {
    digitalWrite(LEDleft, HIGH);
  }
  else if (modEL < 14 || modEL > 36) {
    digitalWrite(LEDleft, LOW);
  }
  if (modER >= 14 && modER <= 36) {
    digitalWrite(LEDright, HIGH);
  }
  else if (modER < 14 || modER > 36) {
    digitalWrite(LEDright, LOW);
  }
}

int distCalcL()  {

  pinMode(SonarL, INPUT);

  for(int i = 0; i < arraysizeL; i++)
  {								    
    digitalWrite(TriggerL, HIGH);
    pulseL = pulseIn(SonarL, HIGH);
    rangevalueL[i] = pulseL/147;
    delay(10);
  }
  //  Serial.print("Unsorted: ");
  //  printArrayL(rangevalueL,arraysizeL);
  isortL(rangevalueL,arraysizeL);
  //Serial.print("Sorted: ");
  // printArrayL(rangevalueL,arraysizeL);
  modEL = modeL(rangevalueL,arraysizeL);

  Serial.print("The mode/medianL is: ");
  Serial.print(modEL);
  Serial.println();
  delay(100);
  digitalWrite(TriggerL, LOW);
}
/*-----------Functions------------*///Function to print the arrays.
void printArrayL(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 isortL(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 modeL(int *x,int n){

  int i = 0;
  int countL = 0;
  int maxCountL= 0;
  int modeL = 0;
  int bimodalL;
  int prevCountL = 0;

  while(i<(n-1)){

    prevCountL=countL;
    countL=0;

    while(x[i]==x[i+1]){

      countL++;
      i++;
    }

    if(countL>prevCountL&countL>maxCountL){
      modeL=x[i];
      maxCountL=countL;
      bimodalL=0;
    }
    if(countL==0){
      i++;
    }
    if(countL==maxCountL){//If the dataset has 2 or more modes.
      bimodalL=1;
    }
    if(modeL==0||bimodalL==1){//Return the median if there is no mode.
      modeL=x[(n/2)];
    }

    return modeL;
  }
} 
int distCalcR()  {

  pinMode(SonarR, INPUT);

  for(int i = 0; i < arraysizeR; i++)
  {								    
    digitalWrite(TriggerR, HIGH);
    pulseR = pulseIn(SonarR, HIGH);
    rangevalueR[i] = pulseR/147;
    delay(10);
  }
  //  Serial.print("Unsorted: ");
  //  printArrayR(rangevalueL,arraysizeL);
  isortR(rangevalueR,arraysizeR);
  //Serial.print("Sorted: ");
  // printArrayR(rangevalueL,arraysizeL);
  modER = modeR(rangevalueR,arraysizeR);

  Serial.print("The mode/medianR is: ");
  Serial.print(modER);
  Serial.println();
  delay(100);
  digitalWrite(TriggerR, LOW);
}
/*-----------Functions------------*///Function to print the arrays.
void printArrayR(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 isortR(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 modeR(int *x,int n){

  int i = 0;
  int countR = 0;
  int maxCountR= 0;
  int modeR = 0;
  int bimodalR;
  int prevCountR = 0;

  while(i<(n-1)){

    prevCountR=countR;
    countR=0;

    while(x[i]==x[i+1]){

      countR++;
      i++;
    }

    if(countR>prevCountR&countR>maxCountR){
      modeR=x[i];
      maxCountR=countR;
      bimodalR=0;
    }
    if(countR==0){
      i++;
    }
    if(countR==maxCountR){//If the dataset has 2 or more modes.
      bimodalR=1;
    }
    if(modeR==0||bimodalR==1){//Return the median if there is no mode.
      modeR=x[(n/2)];
    }

    return modeR;
  }
}

I find your code a little cumbersome to read, as there seems to be a lot of extraneous matter
in there. IE, you seem to have taken the code for one sonar and simply cloned it and given
it a different name for the other, same for the sorts, the prints, and the mode functions.

I did notice what appears to be an error, twice repeated, due to cloning.

    if(countL>prevCountL&countL>maxCountL){

s/b:

    if( countL > prevCountL && countL > maxCountL ) {

or as I prefer:

    if( (countL > prevCountL) && (countL > maxCountL) ) {

Difficult to read when all cluttered together, plus I hate precedence tables.

As I mentioned earlier, my Maxsonars also read about 2" too low. I don't think filtering or
averaging will solve that problem. It's possibly/probably a problem with the units themselves.

I know what you're saying about being difficult to read. I apologize for that. Also about the cloned code with different names, you have to do that or it doesn't work for the second sensor, the right in this case.

As 3 examples, you're passing your data arrays into the functions and using only
local variables, the only difference is L or R in the function name.

void printArrayR(int *a, int n)
int modeR( int *x,int n )
void isortR(int *a, int n)

The following can also be changed to pass in the sonar i.d. and int rangevalue[] R,L arrays

int distCalcL()

It can be simplified.