Serial Communication Inconsistencies

Hello, thank you in advance for taking the time to try to help me remedy my current issue.

I'm utilizing a U81 Laser Range Finder from Chengdu JRT with an Arduino Mega 2560 R3. My current circuit is simply the U81 connected to 3.3V, and Tx connected to Tx1 (Pin 18), while Rx is connected to Rx1 (Pin 19).

In order to synchronize the Arduino with the laser range finder, I have written a short sketch which attempts to create a state machine to send a command to start a single automatic measurement cycle, and then await the header of the response. From there, it has to pull the payload from bytes 6, 7, 8, and 9. Once that's been done, those four bits are supposed to be equivalent to the measurement in millimeters.

I'm having trouble with getting consistent payload distances, as well as having trouble performing a calibration of the data to actually match the actual distance it's measuring currently which is ~1000mm.

I'm a bit of a noob with this aside from the occasional hobby project so any and all suggestions are appreciated! In the attempt to give as much information as possible, I'll be attaching the manufacturer datasheet pertaining to sending and receiving these measurement commands, my code, and a sample output from my Serial Monitor.


SerialMonitorOutput

// No need to include SoftwareSerial library, as we will use hardware UART (Serial1)

// COMMANDS FROM DATASHEETs
byte one_shot_auto[9] = {0xAA, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x21};
byte reply_measurement_results[12] = {0xAA, 0x00, 0x00, 0x22, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01};

// VARs FOR WORKING WITH DATA
unsigned long Data = 0;
unsigned int SignalQuality = 0;
byte Read[12];

// Global variable to store the calibration offset
unsigned long calibrationOffset = 0;

// Function to send one-shot auto mode command
void sendOneShotAutoCommand() {
  delay(10); // Small delay to ensure sensor is ready to receive
  Serial1.write(one_shot_auto, 9);
}

// Function to read the response from the sensor
bool readSensorResponse() {
  // Wait for the complete reply from the sensor with a timeout of 500 milliseconds
  unsigned long startTime = millis();
  int bytesRead = 0;
  bool headerFound = false;
  int startIdx = 0;

  while (bytesRead < 12 && millis() - startTime < 1000) {
    if (Serial1.available()) {
      byte currentByte = Serial1.read();

      if (!headerFound) {
        if (currentByte == 0xAA) {
          headerFound = true;
          startIdx = bytesRead;
        }
      }

      if (headerFound) {
        Read[bytesRead] = currentByte;
        bytesRead++;

        // If we have received 12 bytes, check if it's a valid reply
        if (bytesRead == 12) {
          // Process the reply and extract the measurement data
          if (Read[startIdx + 1] == 0x00 && Read[startIdx + 2] == 0x00 && Read[startIdx + 3] == 0x22 && Read[startIdx + 4] == 0x00 && Read[startIdx + 5] == 0x03) {
            Data = (unsigned long)Read[startIdx + 6] << 24 | (unsigned long)Read[startIdx + 7] << 16 | (unsigned long)Read[startIdx + 8] << 8 | Read[startIdx + 9];
            SignalQuality = (unsigned int)Read[startIdx + 10] << 8 | Read[startIdx + 11];
            return true;
          }
        }
      }
    }
  }

  return false;
}

// Function to calibrate the payload distance
unsigned long calibrateDistance(unsigned long rawDistance) {
  // Calculate the calibration offset if not already calculated
  if (calibrationOffset == 0) {
    // Replace '1000' with the actual distance in millimeters from the target
    unsigned long actualDistance = 1000;
    // Calculate the offset by finding the difference between measured and actual distance
    calibrationOffset = actualDistance - rawDistance;
  }

  // Apply the calibration offset to the raw distance
  unsigned long calibratedDistance = rawDistance + calibrationOffset;

  return calibratedDistance;
}

// Function to print measurement data and signal quality
void printMeasurementData() {
  // Extract the payload distance (measurement data)
  unsigned long payloadDistance = (unsigned long)Read[6] | (unsigned long)Read[7] << 8 | (unsigned long)Read[8] << 16 | (unsigned long)Read[9] << 24;
  // Calibrate the payload distance
  unsigned long calibratedDistance = calibrateDistance(payloadDistance);
  
  // Print the payload distance values
  Serial.print("Payload Distance (Raw): ");
  Serial.println(payloadDistance);
  Serial.print("Payload Distance (Calibrated): ");
  Serial.println(calibratedDistance);
  
  // Extract the payload count (number of valid data points)
  int payloadCount = (Read[5] << 8) | Read[4];
  
  // Extract the payload signal quality
  int payloadSignalQuality = (Read[11] << 8) | Read[10];

  Serial.print("Payload Count: ");
  Serial.println(payloadCount);

  Serial.print("Payload Signal Quality: ");
  Serial.println(payloadSignalQuality);

  // If the payload count is not 3, print the raw data
  if (payloadCount != 3) {
    Serial.println("Invalid response or no response received.");
    printRawData();
  }
}

void printRawData() {
  // Add debug information
  unsigned int payloadCount = (Read[5] << 8) | Read[4];
  unsigned long payloadDistance = (unsigned long)Read[9] << 24 | (unsigned long)Read[8] << 16 | (unsigned long)Read[7] << 8 | Read[6];
  Serial.print("Payload Count: ");
  Serial.println(payloadCount);
  Serial.print("Payload Distance: ");
  Serial.println(payloadDistance);
}

//==========

void setup() {
  Serial.begin(19200); // Set baud rate for Serial Monitor
  delay(2000);
  Serial1.begin(19200); // Set baud rate for communication with the laser range finder
}

enum State {
  IDLE,
  MEASURING,
};

State currentState = IDLE;
unsigned long previousMillis = 0;

//==========

void loop() {
  switch (currentState) {
    case IDLE:
      sendOneShotAutoCommand();
      currentState = MEASURING;
      previousMillis = millis(); // Store the current time for delay
      break;

    case MEASURING:
      if (millis() - previousMillis >= 500) { // Increase the timeout to 500ms
        bool responseReceived = readSensorResponse();
        if (responseReceived && Read[5] == 0x04) { // Check for valid payload count (4 bytes for payload distance)
          printMeasurementData();
        } else {
          Serial.println("Invalid response or no response received.");
          printRawData();
        }
        currentState = IDLE;
        delay(100); // Add a delay before the next measurement to avoid rapid transitions
      }
      break;
  }
}

Just a thought. Here

        if (currentByte == 0xAA) {
          headerFound = true;
          startIdx = bytesRead;
        }

I would expect to see you to reset bytesRead as it is used as an index in your Read array.

At a glance through the tiny window here.

a7

I set bytesRead = 0 a few lines above that in the beginning of the bool readSensorResponse function. Is there a better way to do this?

Thanks for taking the time to look through this, I appreciate any and all help :slight_smile:

Sorry, small window. It looked to me like you were locked in the while loop.

Anyway, made you look. :wink:

Now look at the payload size bytes. The document shows 3 where the payload is 4 bytes, and 1 where it is 2.

Another straw perhaps.


If I were there, I would crank up the serial monitor baud rate to 115200 and start looking at what's really coming in and whether/how it is being properly stashed for subsequent analysis.

These are world wide fora, maybe all the ppl who will spot something immediately are asleep…

a7

Since I have no life, I rigged up a means to feed a known character stream into your machinery. Ask me about it some day.

I fed it some random characters, then the 13 (!) byte sequence in Table 6-20. I found what I believe to be a few problems.

The checksum byte is mislabelled in the table, it is at index 12, not 8, and will be the thirteenth character arrived. Maybe not yet any trouble.

The startIdx, by logic, will always be zero, I am not sure why you need bytesRead and startIdx. Something I will look at.

Did. If startIdx ever is not zero, you are running off the Read array with some indices.

    SignalQuality = (unsigned int)Read[startIdx + 10] << 8 | Read[startIdx + 11];
  // Extract the payload count (number of valid data points)
  int payloadCount = (Read[5] << 8) | Read[4];

is inconsistent with

    if (responseReceived && Read[5] == 0x03) { // Check for valid payload count (4 bytes for payload distance)     

It looks like you are assembling the four byte number backwards also, viz:

  unsigned long payloadDistance = (unsigned long)Read[6] << 24 | (unsigned long)Read[7] << 16 | (unsigned long)Read[8] << 8 | Read[9];

and

  unsigned long payloadDistance = (unsigned long)Read[6] | (unsigned long)Read[7] << 8 | (unsigned long)Read[8] << 16 | (unsigned long)Read[9] << 24;

so you aren't even consistently wrong.

With those changes, I feed it

byte fakeData[] = {0x1, 0x2, 0x3, 0x4, // leading garbage
0xaa, 0x0, 0x0, 0x22, 0x0, 0x3, 0x01, 0xa2, 0xa3, 0xa4, 0x1, 0x1, 0xff,
0x4, 0x3, 0x2, 0x1,  // trailing garbage
};

and it reports

 fake it 
s1 begin
 one shot whatever 
1 read 2 read 3 read 4 read AA read header byte seen boss!
startIdx 0
 stash AA at index  0
0 read  stash 0 at index  1
0 read  stash 0 at index  2
22 read  stash 22 at index  3
0 read  stash 0 at index  4
3 read  stash 3 at index  5
1 read  stash 1 at index  6
A2 read  stash A2 at index  7
A3 read  stash A3 at index  8
A4 read  stash A4 at index  9
1 read  stash 1 at index  10
1 read  stash 1 at index  11
claim we have received 12 bytes
3
Payload Distance (Raw): 1A2A3A4
Payload Distance (Calibrated): 3E8
Payload Count: 3
Payload Signal Quality: 257
 one shot whatever 
FF read 4 read 3 read 2 read 1 read all out of fake data

I did not dig into the 12 versus 13 thing.

And here I will leave it, as it seems there are some thinkos that are def hanging you up.

It's not so hot this day that I will blame the heat if all this is barking in the wrong key.

Fun!

a7

Hi,

Tx of the U81 to Rx1 of the 2560.
Rx of the U81 to Tx1 of the 2560.

Tom.. :smiley: :+1: :coffee: :australia:

Be careful with the logic levels. If you supply 3.3V to the U81, can its RX input sustain 5V from the Arduino according to the datasheet?

Oh wow, thank you for doing that full analysis, I really appreciate it!
You've given me quite a few things to go hunt down and troubleshoot, thanks again!

Well for the full analysis you can't afford me. :wink:

So far, just doing what I'd do if it were me, step by step. The addition of high speed serial printing is key - many of my sketches are very (very!) chatty.

Do you have a link to the document for which you pulled those tables? The device you playing with looks very interesting and not so spendy as to rule out a request to Santa.

a7

Haha you're probably right :smiley:

Your approach definitely helped me identify some key issues and I think I'm on the right track now, thank you again!

Here's the manual for the U81, hope it can lead to some fun projects for you also! It seems to be a decent balance of price to performance from what I've seen compared to others out there.

U81 Laser Distance Sensor USER MANUAL.pdf (3.1 MB)

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.