Arduino Due DMA Serial Port

Hey there,

I’m currently on a project about a lidar sensor.
For this project I used the RPLidar Arduino Library, but I think the code isn’t fast enough because I get many incorrect measurements in the serial monitor.
I asked the support and they already told me to change the code andy try DMA.
Could somebody give me some advise how to change it to DMA?

Here’s the RPLidar Header out of the library:

#pragma once

#include "Arduino.h"
#include "inc/rptypes.h"
#include "inc/rplidar_cmd.h"

struct RPLidarMeasurement
{
    float distance;
    float angle;
    uint8_t quality;
    bool  startBit;
};

class RPLidar
{
public:
    enum {
        RPLIDAR_SERIAL_BAUDRATE = 115200,  
        RPLIDAR_DEFAULT_TIMEOUT = 500,
    };
    
    RPLidar();  
    ~RPLidar();

    // open the given serial interface and try to connect to the RPLIDAR
    bool begin(HardwareSerial &serialobj);

    // close the currently opened serial interface
    void end();
  
    // check whether the serial interface is opened
    bool isOpen(); 

    // ask the RPLIDAR for its health info
    u_result getHealth(rplidar_response_device_health_t & healthinfo, _u32 timeout = RPLIDAR_DEFAULT_TIMEOUT);
    
    // ask the RPLIDAR for its device info like the serial number
    u_result getDeviceInfo(rplidar_response_device_info_t & info, _u32 timeout = RPLIDAR_DEFAULT_TIMEOUT);

    // stop the measurement operation
    u_result stop();

    // start the measurement operation
    u_result startScan(bool force = false, _u32 timeout = RPLIDAR_DEFAULT_TIMEOUT*2);

    // wait for one sample point to arrive
    u_result waitPoint(_u32 timeout = RPLIDAR_DEFAULT_TIMEOUT);
    
    // retrieve currently received sample point
    
    const RPLidarMeasurement & getCurrentPoint()
    {
        return _currentMeasurement;
    }

protected:
    u_result _sendCommand(_u8 cmd, const void * payload, size_t payloadsize);
    u_result _waitResponseHeader(rplidar_ans_header_t * header, _u32 timeout);

protected:
    HardwareSerial * _bined_serialdev;  
    RPLidarMeasurement _currentMeasurement;
};

And that’s the code I used to read the measurements:

#include <RPLidar.h>

// You need to create an driver instance
RPLidar lidar;

// The PWM pin for control the speed of RPLIDAR's motor.
// This pin should connected with the RPLIDAR's MOTOCTRL signal
#define RPLIDAR_MOTOR 2

bool transmit = false;
byte incomingByte;
int x = 0;
int y = 0;

void setup()
{
  // bind the RPLIDAR driver to the arduino hardware serial
  Serial1.begin(115200);
  lidar.begin(Serial1);

  // initialize both serial ports:
  Serial.begin(115200);    // Initialize Native USB port

  // set pin modes
  pinMode(RPLIDAR_MOTOR, OUTPUT);
  analogWrite(RPLIDAR_MOTOR, 255);
}

void loop() {
  if (IS_OK(lidar.waitPoint()))
  {
    float distance = lidar.getCurrentPoint().distance; //distance value in mm unit
    float angle    = lidar.getCurrentPoint().angle; //anglue value in degree
    bool  startBit = lidar.getCurrentPoint().startBit; //whether this point is belong to a new scan
    byte  quality  = lidar.getCurrentPoint().quality; //quality of the current measurement

    if (Serial.available() > 0)
    {
      incomingByte = Serial.read();
      transmit = !transmit;
    }

    //Serial.print("dist: ");
    Serial.print(distance);
    Serial.print(";");
    //Serial.print("\tangle: ");
    Serial.print(angle);
    Serial.print(";");
    //Serial.print("\tsbit: ");
    //Serial.print(startBit);
    //Serial.print(";");
    //Serial.print("\tquality: ");
    Serial.print(quality);
    Serial.print("\n");

  }

  else
  {
    analogWrite(RPLIDAR_MOTOR, 0); //stop the rplidar motor

    // try to detect RPLIDAR...
    rplidar_response_device_info_t info;
    if (IS_OK(lidar.getDeviceInfo(info, 100)))
    {
      // detected...
      lidar.startScan();

      // start motor rotating at max allowed speed
      analogWrite(RPLIDAR_MOTOR, 255);
      delay(1000);
    }
  }
}

I attached the whole library to the post because I’m not sure where I need to make some changes to use the DMA.

Thank you :slightly_frowning_face:

rplidar_arduino_library.zip (21.6 KB)

drehm0ment:
// bind the RPLIDAR driver to the arduino hardware serial
Serial1.begin(115200);
lidar.begin(Serial1);

// initialize both serial ports:
Serial.begin(115200); // Initialize Native USB port

1/ Can't you increase Serial1 baud rate (I guess Serial1 is used for the communication with the Lidar device), or is this limited to 115200 ?

2/ You can increase up to 250000 baud Serial communication with your PC monitor

3/ Why delay(1000) ? This is a blocking code.

1/ and 2/ I increased both to 250000. It works, but no big changes.

Then I also increased the baud rate in the Lidar Library to 250000, but then it doesn't work anymore.

drehm0ment:

class RPLidar

{
public:
    enum {
        RPLIDAR_SERIAL_BAUDRATE = 115200, 
        RPLIDAR_DEFAULT_TIMEOUT = 500,
    };

3/ delay(1000) was already written in the libraries example code. I just tried to comment it out, but nothing changed at all.

Did you try to print your results on the PC monitor thru SerialUSB (the Native USB port) instead of the programming port ? The printing process is way much faster with SerialUSB.

I just tried it. I added SerialUSB.begin(250000) at the beginning and changed everything to SerialUSB.print but the result is the same.
Here's an example of the angle measurement:

120,00
121,09
122,22
123,34
124,47
125,55
126,67
127,78
128,89
130,02
131,09
132,20
245,34
76,52
136,61
104,53
148,41
340,55
86,58

It's all fine but then suddenly it begins to jump randomly up and down until it becomes stable again.

I know some of our users want to print data by using the serial monitor in Arduino IDE , this library only could get all the data , but when you print it out , the MCU would wait until the print function ends, then it would miss the next period data. To solve that case , user could use DMA to deal with the process of receive and send data.

That's what the RPLidar support told me as I asked them about this problem.

I am not so sure that a UART DMA would solve the issue, there must be something wrong elswhere. Anyway, I have provided example sketches with UART DMA in the DUE sub forum.

drehm0ment:
if (Serial.available() > 0)
{
incomingByte = Serial.read();
transmit = !transmit;
}

What the use of these lines, since you always wait for results to be Serial printed on PC monitor ?

Comment these lines, and see what happens.

ard_newbie:
Comment these lines, and see what happens.

I just did and still no changes

Hi,
before trying to optimize your code using DMA, you should ensure that the problem is really a real time problem due to the Serial.

It is obvious that using a lot of print commands in your code is time consuming but whether they are a problem or not depends only on your data reception rate from the rplidar. I don't know how rplidar works, but the way your code is written let thinking that you restart it at each main loop execution while no data is received which seems to me more problematic than sending data on the Serial.

So to be sure the problem can be solved by DMA, you should insert time information in your code to measure the data reception rate and the duration of your data execution . Compare them and you will see if you have free time or not.

As an exemple you could set a code like this :

Serial.println("Rx: "+String(millis()));

just after the opening bracket of if (IS_OK(lidar.waitPoint()))

and
Serial.println("Tx End: "+String(millis()));
just before the closing bracket.

You can also add time information in the rplidar control part (what is in the else condition) to help understanding where time is spent.

If you have time available to execute the rplidar control part you should see a difference between the" Tx End" time and the next "Rx".

If you have no time available, it means that new data arrived before the end of processing and you may have no time to execute the rplidar control part. So an optimization of Serial is necessary.

Finally, to see if DMA has a chance to solve your problem, comment most of your Serial.print except these debug lines and something that will enable you to check if you still have the problem.

If it is still there, DMA will not fix it...

I know I'm digging up sort of an old thread, but has anyone gotten the RPLidar A1 to work properly with the Arduino yet? I've connected mine to an Arduino Mega, and while I am able to receive data from the sensor, like the original poster, my readings are all over the place (they often don't seem accurate), leading me to believe that the Arduino doesn't have the speed to handle the large amount of data coming from the RPLidar sensor.

I feel like this is a sensor that might require something with the speed of a Raspberry Pi to really work properly.

I’m now working with the Arduino Due and Matlab Simulink and I almost never get wrong measurements. So it’s possible with an Arduino.

In the Arduino IDE, I could improved my plot a little bit with some kind of “filters” like:

if (distance && distance<=4000 && quality==15 && angle<=360)
    {
      SerialUSB.print(distance);
      SerialUSB.print(";");
      SerialUSB.print(angle);
      SerialUSB.print(";");
      SerialUSB.print(quality);
      SerialUSB.print("\n");
    }

It’s better then before, but there are still a lot of wrong measurements…

I think there is a way to improve the Arduino IDE code until there aren’t any faults left, but unfortunately I personally didn’t get it.

drehm0ment:
I’m now working with the Arduino Due and Matlab Simulink and I almost never get wrong measurements. So it’s possible with an Arduino.

In the Arduino IDE, I could improved my plot a little bit with some kind of “filters” like:

if (distance && distance<=4000 && quality==15 && angle<=360)

{
     SerialUSB.print(distance);
     SerialUSB.print(";");
     SerialUSB.print(angle);
     SerialUSB.print(";");
     SerialUSB.print(quality);
     SerialUSB.print("\n");
   }



It's better then before, but there are still a lot of wrong measurements...

I think there is a way to improve the Arduino IDE code until there aren't any faults left, but unfortunately I personally didn't get it.

Yeah so basically you’re just filtering out any wildly wrong measurements? I get that I can do that, but like you said there is still a lot of questionable data.
I’ve seen this sensor perform mapping very nicely when interfacing with other processors like a Raspberry Pi or a PC, I just wish I knew what calculations or filters to put on the raw data to be able to weed out the junk. Thanks though.

Ok actually I think I am getting some decent data from the sensor now. I took any code out of the program that does anything with the serial link between the Arduino and computer while the sensor is feeding measurements to the Arduino and it's storing them to variables. I don't even start the serial link to the computer until it's finished doing that. I just let it do its thing, store its measurements to an array of variables, and only after it's all done do I then open the serial connection to the computer and display all the measurements in the serial monitor. Seems like the measurements are much faster and more accurate doing it this way than if you are doing anything with the serial link between the Arduino and computer while the sensor is taking its measurements.

But also similar to you, I'm throwing away any measurements that fall outside the rated range of the sensor 0.5meters - 6 meters. I'm also throwing away any measurements that are greater than 360 degrees because, well that's not possible.

Hello, has anybody had any development using the RPLIDAR sensor? I am using one connected to a Mega for some basic object detection.

I have added some major filter so it is only looking for data over a small angle (say 90 deg +/- 2 deg) although it works its not I would call reliable.

A couple of things I notice is the quality never goes above 15 (I think the max should be 255)

To reduce the output pace from the LIDAR device, slow-down the LIDAR motor.
To increase the baud rate between Arduino DUE Serial1 AND the LIDAR device , you have to alter RPLIDAR_SERIAL_BAUDRATE in RPLIDAR.h AND alter the serial baud rate inside the LIDAR device (obviously to the exact same baud rate on both sides). Ask ROBOPEAK how you can alter the Serial baud rate INSIDE the LIDAR device. Hardware Serial (Serial1) can easily sustain 2 million bauds and higher.
Anyway I have provided an USART DMA example somewhere in the DUE sub forum, but I'm pretty sure that it would be a bunch of useless complexities.

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