Laser rangefinder code problems

I am currently working with my professor on putting together ultrasonic and laser rangefinders. We have got the ultrasonic rangefinder working great, but we do not have the laser rangefinder working correctly. We are using a Lightware AL-01 laser rangefinder on an Arduino Uno board and that is connected to our main board (Mega 2560). For some reason the output from the laser is unreadable in our data and shows up as B-99999E and stuff like that. Below is the code for our laser rangefinder. Can someone please tell me if there is a mistake in our code. Thanks

/*
 *---------------------------------------------------------
 * AL_01_Arduino_Laser_Range_Finder
 * Rev0
 * 25/06/2012
 *
 * This program includes the following modules:
 *
 *  1. AL_01_Arduino_Laser_Range_Finder_Rev0.ino - Top level
 *  2. laser.ino  - communications with the DS00VQ100 chip and control functions
 *  3. serial.ino  - USB communications functions
 *  4. settings.ino  - settings and EEPROM functions
 *  5. utilities.ino  - other useful functions
 *  6. constants.h  - constant values available to all functions
 *  7. globals.ino  - definitions of global variables
 *  8. globals.h  - makes global variables available to all functions
 * 
 *---------------------------------------------------------
 */

#include "constants.h"
#include "globals.h"
#include <EEPROM.h>
#include <SPI.h>
#include <stdio.h>
#include <avr/pgmspace.h>
#include <Wire.h>
//#include <I2C_Anything.h>
//#include "DHT.h"
//#include <SoftwareSerial.h>

const boolean debug = false;

// I2C Config
const int laserControlI2CAddress = 2;
char LaserRange[6];
String LaserRangeTX = "";

// Hardware pin assignments
  // These four transmit each digit of the range distance via PWM values.
  const int mmPin = 3;  // thousandth-of-meter (millimeter) digit
  const int cmPin = 5;  // hundredth-of-meter (centimeter) digit
  const int dmPin = 6;  // tenth-of-meter (decimeter) digit
  const int  mPin = 9;  // meters (meters) digit
  const int DkPin = 10; // Tens of meters (dekameters) digit
  //const int HeartbeatLEDPin = 11;

// Status readout config

//int HeartbeatBrightness = 0;

int lastLaserRangeInMM = 0;
int MaxLaserRangeInMM = 25000;
String LaserRangeString = "";

/*
// Temperature + humidity sensor
  // Hardware sensor config
  #define DHTPIN 11 // what pin
  #define DHTTYPE DHT22 // DHT 22 (AM2302)
  DHT dht(DHTPIN, DHTTYPE);
  
  // Sensor software config
  int refreshInterval = 1000; // every 1 seconds
  int lastTempHumidReadingTimestamp = 3000; // start checking after 3 seconds
  
  float h = 0.0;
  float t = 0.0;
  
  int humInt = 0;
  int tempInt = 0;
  
  char temp[5];
  char humid[5];
  String tempHumidTX = "";
// End Temperature + humidity sensor
*/

void setup()
{
  pinMode( mmPin, OUTPUT );
  pinMode( cmPin, OUTPUT );
  pinMode( dmPin, OUTPUT );
  pinMode(  mPin, OUTPUT );
  pinMode( DkPin, OUTPUT );
  
  delay(3000);             // wait for power to stabilize
  
  //initialiseHardware();    // utilities.ino
  Serial.begin(9600);

  // SPI
  pinMode(SPI_CHIPSELECT_PIN, OUTPUT);
  digitalWrite(SPI_CHIPSELECT_PIN, LOW);
  pinMode(DATA_READYN_PIN, INPUT);
  SPI.begin();
  
  intSettings[FOOTER] = ASYNC_FOOTER;
  digitalWrite(SPI_CHIPSELECT_PIN, HIGH);
  delay(1);
  for (int j = 0; j < 14; ++j)
    spiRead[j] = SPI.transfer((byte)intSettings[j]);
  delay(1);
  digitalWrite(SPI_CHIPSELECT_PIN, LOW);
  initialiseSettings();    // settings.ino
  
  
  Wire.begin(laserControlI2CAddress);
  Wire.onRequest(requestEvent);
  
  //dht.begin();
  
  // Handle configuration via serial to laptop
  if (intSettings[DISPLAY_MODE] == 4)
    displaySettingsMenu();
    
   //Serial.println ( "LASER UNIT AWAITING COMMANDS." );
}

void loop()
{
  /*
  if ( millis() > lastTempHumidReadingTimestamp +  refreshInterval ) {
    lastTempHumidReadingTimestamp = millis();
    // Reading temperature or humidity takes about 250 milliseconds!
    // Sensor readings may also be up to 2 seconds 'old' (it's a very slow sensor)
    h = dht.readHumidity();
    t = dht.readTemperature();
    
    humInt = h * 1;
    tempInt = t * 1;
    
    if ( debug ) {
      // check if returns are valid, if they are NaN (not a number) then something went wrong!
      if (isnan(t) || isnan(h)) {
        Serial.println("Failed to read from DHT");
      } else {
        dtostrf(t, 9, 1, temp);
        dtostrf(h, 5, 1, humid);
        Serial.print("Humidity: ");
        Serial.print(h);
        Serial.print(" %\t");
        Serial.print("Temperature: ");
        Serial.print(t);
        Serial.println(" *C");
      } // End debug output
    } // End debug check
  } // End refreshInterval check
  */
  //lcd.setCursor(0, 1);
  //lcd.print(lastLaserRangeInMM);
  handleKeypress();    // serial.ino  
  handleSPIResult();   // laser.ino
  
  lastLaserRangeInMM = (int) (distance * 1000.0); // This step removes the decimal in the distance float.
  
  if ( lastLaserRangeInMM <= 0 ) {
    lastLaserRangeInMM = -9999;
  } else if ( lastLaserRangeInMM >= MaxLaserRangeInMM ) {
    lastLaserRangeInMM = MaxLaserRangeInMM;
  }
  
  LaserRangeString = (String)lastLaserRangeInMM;
  //Serial.println(LaserRangeString);
  
  int FirstDigit = CharConvertedToDigit(LaserRangeString.charAt(0));
  int SecondDigit = CharConvertedToDigit(LaserRangeString.charAt(1));
  int ThirdDigit = CharConvertedToDigit(LaserRangeString.charAt(2));
  int FourthDigit = CharConvertedToDigit(LaserRangeString.charAt(3));
  int FifthDigit = CharConvertedToDigit(LaserRangeString.charAt(4));
  
  int DekameterDigit = 0;
  int MeterDigit = 0;
  int DecimeterDigit = 0;
  int CentimeterDigit = 0;
  int MillimeterDigit = 0;
  
  if ( lastLaserRangeInMM < 10 && lastLaserRangeInMM >= 0) {
    LaserRangeString = "0000" + LaserRangeString;
    //MillimeterDigit = FirstDigit;
    
  } else if ( lastLaserRangeInMM < 100 && lastLaserRangeInMM >= 0 ) {
    LaserRangeString = "000" + LaserRangeString;
    //CentimeterDigit = FirstDigit;
    //MillimeterDigit = SecondDigit;
    //Serial.println (LaserRangeString);
  } else if ( lastLaserRangeInMM < 1000 && lastLaserRangeInMM >= 0 ) {
    LaserRangeString = "00" + LaserRangeString;
    //DecimeterDigit = FirstDigit;
    //CentimeterDigit = SecondDigit;
    //MillimeterDigit = ThirdDigit;
    //Serial.println (LaserRangeString);
  } else if ( lastLaserRangeInMM < 10000 && lastLaserRangeInMM >= 0 ) {
    LaserRangeString = "0" + LaserRangeString;
    //DekameterDigit = 0;
    //MeterDigit = SecondDigit;
    //DecimeterDigit = ThirdDigit;
    //CentimeterDigit = FourthDigit;
    //MillimeterDigit = FifthDigit;
    //Serial.println (LaserRangeString);
  } else if ( lastLaserRangeInMM <= MaxLaserRangeInMM ) {
    //Serial.println (LaserRangeString);
    //DekameterDigit = FirstDigit;
    //MeterDigit = SecondDigit;
    //DecimeterDigit = ThirdDigit;
    //CentimeterDigit = FourthDigit;
    //MillimeterDigit = FifthDigit;
  } else if (lastLaserRangeInMM < 0 ) {
    LaserRangeString = "-7777";
    //MillimeterDigit = 128; //"Negative Range: -9999" 
    //Serial.println (LaserRangeString);
  } else {
    LaserRangeString = "-8888" ;
  } 
  LaserRangeString = "B" + LaserRangeString + "E";
  Serial.println (LaserRangeString);
  
  /*
  analogWrite( DkPin, DekameterDigit * 25); //multiply by 25 to use the full possible range of values for analogWrite (0 - 225, anyway...)
  analogWrite(  mPin, MeterDigit * 25);
  analogWrite( dmPin, DecimeterDigit * 25);
  analogWrite( cmPin, CentimeterDigit * 25);
  analogWrite( mmPin, MillimeterDigit * 25);
  */
 
}


void requestEvent() //12C data transfer
{
  //Serial.print( "Range: " );
  //Serial.println( LaserRangeString );
  //Serial.println("Incoming I2C request");
  String bar = "R";
  bar += LaserRangeString;
  //bar += lastLaserRangeInMM;
  bar += "X";//'\0';
  char foo[bar.length()];
  bar.toCharArray(foo, bar.length());
  Wire.write(foo);
  //Wire.write(temp);
  //Serial.print("I2C requested, sent: ");
  //Serial.println(foo);
}


int CharConvertedToDigit (char input) {
  int tempInt;
  
  if( input == 45 ) {
    tempInt = 128;
    return tempInt;
  } else if( input == 48 ) {
    tempInt = 0;
    return tempInt;
  } else if( input == 49 ) {
    tempInt = 1;
    return tempInt;
  } else if( input == 50 ) {
    tempInt = 2;
    return tempInt;
  } else if( input == 51 ) {
    tempInt = 3;
    return tempInt;
  } else if( input == 52 ) {
    tempInt = 4;
    return tempInt;
  } else if( input == 53 ) {
    tempInt = 5;
    return tempInt;
  } else if( input == 54 ) {
    tempInt = 6;
    return tempInt;
  } else if( input == 55 ) {
    tempInt = 7;
    return tempInt;
  } else if( input == 56 ) {
    tempInt = 8;
    return tempInt;
  } else if( input == 57 ) {
    tempInt = 9;
    return tempInt;
  } 
}

//void PulseStatusLED () {
//  HeartbeatBrightness = (int) (((float)lastLaserRangeInMM / (float)MaxLaserRangeInMM) * 255.0);
//  analogWrite(HeartbeatLEDPin, HeartbeatBrightness);       
//}

The mistake was apparently in not choosing a school with decent leadership…'cause aside from any obvious hardware problems, this should be dirt simple for the most average of programmers. eg. Your professor sucks…

I am in the Forestry department and this is the first time either one of us has written code or anything. If it is so simple, why don’t you just tell me?

If your school is remotely worth it, go get a “professor” to HELP YOU FIND THE ANSWER!

ebsnively:
For some reason the output from the laser is unreadable in our data and shows up as B-99999E and stuff like that.

First let's look at these parts of the code ... ``` LaserRangeString = "B" + LaserRangeString + "E"; ``` and
  lastLaserRangeInMM = (int) (distance * 1000.0); // This step removes the decimal in the distance float.
 
  if ( lastLaserRangeInMM <= 0 ) {
    lastLaserRangeInMM = -9999;
  } else if ( lastLaserRangeInMM >= MaxLaserRangeInMM ) {
    lastLaserRangeInMM = MaxLaserRangeInMM;
  }

I think it’s a safe guess that the variable lastLaserRangeInMM does indeed = 0. Why ? It could be you’re getting zero back from the device. But also look at the 1’st line in the code box above. A variable of type integer can only hold values from 32+k to -32-k. So if the variable distance is > 32 then that multiplication will shift the data right out of the variable leaving you with all zeros. I also noted this …

int MaxLaserRangeInMM = 25000;

So am I to infer that the distance is expected to range 1-25 ? Better yet can you find a place in the code where that variable, distance, is set ? [That’s a big huge important hint]

Can you do a normal Serial.println() of the data you’re getting from the laser rangefinder ? Of the variables distance and MaxLaserRangeInMM. Where, in the code, does it get data from the laser ??

Why do you have all the string manipulation ? Is this data being displayed on some odd … well … display ? The String class for Arduino bites. I’ve had problems w/it and have since turned my back on using Strings.

skimask:
If your school is remotely worth it, go get a “professor” to HELP YOU FIND THE ANSWER!

Dude, chill. Nothing he said needs that kind of reaction!

@OP: If you need help with code, it’s best to first try to isolate the problem by removing any code that has nothing to do with what you’re trying to debug before posting. Otherwise, you’re posting big blobs of code that just distract anyone trying to help you from the real problem.

Mee_n_Mac:

  lastLaserRangeInMM = (int) (distance * 1000.0); // This step removes the decimal in the distance float.

if ( lastLaserRangeInMM <= 0 ) {
lastLaserRangeInMM = -9999;
} else if ( lastLaserRangeInMM >= MaxLaserRangeInMM ) {
lastLaserRangeInMM = MaxLaserRangeInMM;
}



A variable of type integer can only hold values from 32+k to -32-k. 



I also noted this ...

int MaxLaserRangeInMM = 25000;

Max range we care about is 25m, so everything above that we just clamp to 25m (25000 mm). Errors in ranging are reported as various negative numbers, so we clamp those all to -9999 (mm).

Better yet can you find a place in the code where that variable, distance, is set ? [That’s a big huge important hint]

distance is set by manufacturer-supplied Arduino library, in decimal meters. That code seems to work fine, and outputs perfectly to serial monitor on laptop.

It seems that the problem is in the receiving Arduino (Mega 2560)'s serial receiving of data:

Init:

  Serial.begin(115200); // Debugging
  Serial2.begin(9600); // Laser

Main Loop:

  // Laser daughterboard serial0 TX --> datalogger mainboard serial2 RX
  if( timeStep - laserReadInterval > lastLaserTimeStep && timeStep > laserReadInterval ) {
    
    laserRangeRX = "";
    lastLaserTimeStep = millis();
    
    dataString += lastLaserTimeStep; //append timestamp to string written to SD card storage
   
    int incoming = Serial2.available(); 
    //Serial.println( "Serial buffer size: " + (String)incoming );
    
    while( incoming > 0 ) { // 5 digits, B=begin, E=End, carriage return, line feed, actual digits
      
      int incomingByte = Serial2.read();
      
      if( incomingByte == -1 ) { //discard
        
        //start of header
        
      } else if ( incomingByte == 13 ) { //discard
        
        // Carriage Return
        
      } else if ( incomingByte == 10 ) { //discard
        
        // Line feed
        
      } else {
        
        if( incomingByte == 66 ) { //signal erasure of nextLaserRangeRX buffer string
          
          nextLaserRangeRX = "B";
          
        } else if ( incomingByte == 69 ) { //signal done writing to next laser range string buffer, overwrite laser range with freshly completed next range buffer
          
          lastLaserTimeStep = millis();
          
          nextLaserRangeRX += "E";
          laserRangeRX = nextLaserRangeRX;
          return;
          
        } else {
          
          nextLaserRangeRX += CharConvertedToDigit( incomingByte );
          
        }
      }
    }
  }

//...other sensors input...

  WriteToCard( dataString );


// Convert characters to digits utility function:


String CharConvertedToDigit ( char input ) {
  String tempInt;
  
  if( input == 45 ) {
    tempInt = "-";
    return tempInt;
  } else if( input == 48 ) {
    tempInt = "0";
    return tempInt;
  } else if( input == 49 ) {
    tempInt = "1";
    return tempInt;

// etc. ...

  } else if( input == 57 ) {
    tempInt = "9";
    return tempInt;
  } else if( input == 66 ) {
    tempInt = "B";
    return tempInt;
  } else if( input == 69 ) {
    tempInt = "E";
    return tempInt;
  } 
}

Output to the SD card mashes multiple laser data transmissions together, sometimes drops digits, etc.:

B01350E //well-formed data

B01350E

B01439E //well-formed data

B01439E

B01439E

B01439E1461E //malformed data

B01439E1461E

B01439E1461E

B0112E

B0112E //malformed data

B0112E

B019E //malformed data

B019E

B019E

B01563E

B01563E

So it looks like the serial communication is sending/receiving too much or too little sometimes, even though the baud rate is 9600 for both TX and RX.

Let me try to understand what’s working and what’s not. You have the laser rangefinder (LRF) connected to an Uno. The

LRF talks to the Uno via a TTL serial link (115k baud). The Uno talks to a Mega via a TTL serial link (9600 baud) ? The distance is sent via these serial links ? As an ASCII encoded data block, 8 times/sec. You can “print” the distance at the ?Uno and/or Mega? and it contains a correct value that will always, has always been 32m or less ?

The problem arises only when trying to write the data to the SD card ? Or when trying to get the distance from the Uno to the Mega, even w/o any SD card activity ongoing ? Or is the distance coming across the AUX serial link, directly from the LRF ?

You are correct on all counts. It seems everything is working quite fine except for getting fully correct messaging from the Uno to the Mega. The example output posted earlier demonstrates that the Uno → Mega serial link is working, but the data aren’t coming through and retaining their structure.

So is what’s happening is that some data comes on from the Uno and then, w/o waiting for the whole message, the writing to the SD card commences … and that action somehow screws up the rest of the data from the Uno ? Could you have the latter wait until the whole message is received (or some reasonable timeout) from the Uno ?