LiDAR, TFMini, attempts to filter values all fail

Hi there! I'm a secondary teacher who's recently been handed the job of trying to teach some 13 y.o.s to code. To keep them engaged I try and come up with a more complex project (they're at the "blink LED level") that they help me code before they have to sit through me hand holding them. (The above is also me apologizing for my awful code, I'm only two steps ahead of my students!)
This week's project is a "speed trap", a simple "If (going to fast) {Alarm = on;}"

I'm using a Mega2560 R3.
I've got a TF mini that I'm using to measure distance at two points which I then convert to cm/s for an output. ITI've got this working reasonably well however occasionally I get ridiculous values (students walk faster than mach1). This is probably the laser missing and the differential between the two measurements becoming massive.

As such I decided to try and filter/smooth the output by averaging three readings and/or using SimpleFilter.h to drop the value that's out of range.

What's odd is when I try to use delay to split the three measurements the whole system stops outputting to Serial after about six reading (I get six lines of reasonably accurate distance measurements) then nothing, no TX light on the arduino, nothing.
NB: I know none of this calculates speed or averages. I need consistent readings before I can tackle that.

void loop() {
  int distance = 0;
  int strength = 0;
  getTFminiData(&distance, &strength);
getTFminiData(&distance, &strength);
while (!distance) {
  getTFminiData(&distance, &strength);
}

getTFminiData(&distance, &strength);
Aa = distance;
delay(10);
getTFminiData(&distance, &strength);
Ab = distance;
delay(10)
  getTFminiData(&distance, &strength);
Ac = distance;

//speed = (((distance2 - Result) * 20)); We're not calcuating speed till I can get consistent distance.
//Serial.println(Result);
Serial.print(Aa);
Serial.print(",");
Serial.print(Ab);
Serial.print(",");
Serial.println(Ac);
}

So I tried using Currentmillis/Previousmillis to set up a set of if statements. But this is where my coding knowledge falls apart. What I expect to happen is to see the first couple of readings have a couple of 0s as the CM/PM ticks up but the serial just outputs a series of Aa = 0, Ab = 0 and Ac = Avalue. If I track the CM/PM I don't see any variance between the two.

NB: PM is defined in setup and is 0 at start.

void loop() {
  int distance = 0;
  int strength = 0;
CM = millis();
getTFminiData(&distance, &strength);
while (!distance) {
  getTFminiData(&distance, &strength);
}

if ((CM - PM) < 10) {
  getTFminiData(&distance, &strength);
  Aa = distance; //Aa is first value to be averaged Ab is 2 etc.
  
}
if ((CM - PM) > 10 && (CM - PM) < 15) {
  getTFminiData(&distance, &strength);
  Ab = distance;
  
}

if ((CM - PM) > 15) {
  getTFminiData(&distance, &strength);
  Ac = distance;
  PM = CM;
}


//Serial.println(Result);  //Ignore please, this is for later.
Serial.print(Aa);
Serial.print(",");
Serial.print(Ab);
Serial.print(",");
Serial.print(Ac);
Serial.print(",");
Serial.print(CM);
Serial.print(",");
Serial.println(PM);

Thank you SO much for your guidance and I apologize in advance for the truly awful code, I'm no doubt making a thousand mistakes I don't even recognize.

Here's all the stuff prior to void loop(). All of it seems to work fine.


#include "TFMini.h"
#include "MD_MAX72xx.h"
#include "MD_Parola.h"
#include "SimpleFilter.h"
TFMini tfmini;

#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
#define MAX_DEVICES 1

#define CLK_PIN 52   // or SCK
#define DATA_PIN 51  // or MOSI
#define CS_PIN 3     // or SS


MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
unsigned long CM;
unsigned long PM;
int CM2;
int PM2;
float distance2;
long speed;
int Aa;
int Ab;
int Ac;
int Avg[3];
int Result;
void getTFminiData(int* distance, int* strength) {
  Serial2.begin(115200, SERIAL_8N1);
  static char i = 0;
  char j = 0;
  int checksum = 0;
  static int rx[9];
  if (Serial2.available()) {
    rx[i] = Serial2.read();
    if (rx[0] != 0x59) {
      i = 0;
    } else if (i == 1 && rx[1] != 0x59) {
      i = 0;
    } else if (i == 8) {
      for (j = 0; j < 8; j++) {
        checksum += rx[j];
      }
      if (rx[8] == (checksum % 256)) {
        *distance = rx[2] + rx[3] * 256;
        *strength = rx[4] + rx[5] * 256;
      }
      i = 0;
    } else {
      i++;
    }
  }
}


void setup() {
  Serial.begin(115200);  //Initialize hardware serial port (serial debug port)
  while (!Serial)
    ;  // wait for serial port to connect. Needed for native USB port only
  Serial.println("Initializing...");
  Serial2.begin(TFMINI_BAUDRATE);  //Initialize the data rate for the SoftwareSerial port
  tfmini.begin(&Serial2);          //Initialize the TF Mini sensor
  // Intialize the object:
  myDisplay.begin();
  // Set the intensity (brightness) of the display (0-15):
  myDisplay.setIntensity(0);
  // Clear the display:
  myDisplay.displayClear();
  myDisplay.setTextAlignment(PA_LEFT);
  myDisplay.setZoneEffect(0, 1, PA_FLIP_UD);
  myDisplay.setZoneEffect(0, 1, PA_FLIP_LR);
  PM = 0;
}

Thanks again!

For anyone in the future who comes across this. I solved the issue by dropping the bandwith for serial 1 to 9600, the TFmini needs 15200 however 9600 is more than enough to send the info we need across.

void setup() {
  Serial.begin(9600);  //Initialize hardware serial port (serial debug port)
  while (!Serial)

I then chose to just do a circular buffer with one measurement per "cycle". While not 100% accurate for actually measuring speed this resulted in a "good enough" output that could tell when I was running or not so long as I mainted speed over ~500ms.

Here's the "Finished" code, I created my own mini font for the numbers so you'll have to take that out for it to work.. I'd still kill for some explanation of how to do a "no delay" version with three readings per loop.


#include "TFMini.h"
#include "MD_MAX72xx.h"
#include "MD_Parola.h"
#include "HalfWidth.h"
#include "SimpleFilter.h"

TFMini tfmini;

#define HARDWARE_TYPE MD_MAX72XX::GENERIC_HW
#define MAX_DEVICES 1

#define CLK_PIN 52   // or SCK
#define DATA_PIN 51  // or MOSI
#define CS_PIN 3     // or SS


MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
long PM = 0;
long CM2;
long PM2 = 0;
float distance2;
long speed;
int Aa = 0;
int Ab = 0;
int Ac = 0;
int Avg[3];
int Result;
long interval = 250;
long DInterval = 2000;

void getTFminiData(int* distance, int* strength) {
  Serial2.begin(115200, SERIAL_8N1);
  static char i = 0;
  char j = 0;
  int checksum = 0;
  static int rx[9];
  if (Serial2.available()) {
    rx[i] = Serial2.read();
    if (rx[0] != 0x59) {
      i = 0;
    } else if (i == 1 && rx[1] != 0x59) {
      i = 0;
    } else if (i == 8) {
      for (j = 0; j < 8; j++) {
        checksum += rx[j];
      }
      if (rx[8] == (checksum % 256)) {
        *distance = rx[2] + rx[3] * 256;
        *strength = rx[4] + rx[5] * 256;
      }
      i = 0;
    } else {
      i++;
    }
  }
}


void setup() {
  Serial.begin(9600);  //Initialize hardware serial port (serial debug port)
  while (!Serial)
    ;  // wait for serial port to connect. Needed for native USB port only
  Serial.println("Initializing...");
  Serial2.begin(TFMINI_BAUDRATE);  //Initialize the data rate for the SoftwareSerial port
  tfmini.begin(&Serial2);          //Initialize the TF Mini sensor
  // Intialize the object:
  myDisplay.begin();
  // Set the intensity (brightness) of the display (0-15):
  myDisplay.setIntensity(0);
  // Clear the display:
  myDisplay.displayClear();
  myDisplay.setFont(HalfWidth);
  myDisplay.setTextAlignment(PA_LEFT);
  myDisplay.setZoneEffect(0, 1, PA_FLIP_UD);
  myDisplay.setZoneEffect(0, 1, PA_FLIP_LR);
  myDisplay.setCharSpacing(0);
}

void loop() {
  int distance = 0;
  int strength = 0;
  long CM = millis();
  long CM2 = millis();
  while (!distance) {
    getTFminiData(&distance, &strength);
  }
  if (CM - PM >= interval) {
    PM = CM;
    getTFminiData(&distance, &strength);
    Aa = distance;
    /*int Avg[] = { Aa, Ab, Ac };
    Result = rawDataFilter(Avg, 3);
    */
    Result = (Aa+Ab+Ac)/3;
    speed = (distance2 - Result)*4;

    Serial.print(" Speed:");
    Serial.print(speed);
    Serial.print(" Result:");
    Serial.print(Result);
    Serial.print(" Dist:");
    Serial.print(Aa);
    Serial.print(",");
    Serial.print(Ab);
    Serial.print(",");
    Serial.println(Ac);

    
  if (speed >= 0 && speed <= 200) {
    myDisplay.print(speed);
  }
  else if (speed > 200)
  {
    myDisplay.print("!");
    delay(2000);
    myDisplay.displayClear();
  }
  /*
  if ((CM2 - PM2) > 5000) {
    myDisplay.displayClear();
    PM = CM;
  }
  */

    Ac = Ab;
    Ab = Aa;
    distance2 = Result;
  }
}

The TF mini pins are 3.3V, right? So you would need a level converter to connect to the Arduino pins.

And looking at the code, I think that you should better use a library. For example this one: GitHub - opensensinglab/tfmini: An Arduino driver for the Benewake TFMini time-of-flight distance sensor
Look at the example code: tfmini/examples/BasicReading/BasicReading.ino at master · opensensinglab/tfmini · GitHub

It takes cares of the readings and the low level complexities.

Beside that, what you do with the function getTFminiData and the static counter static char i = 0; is confusing and over complicated.

If I understand it right, you are calling the function many times and the counter get's increased at each function call. So you have to call the function many times to finish the work once.
Maybe initially it was designed this way to work with the main loop without blocking it during the transmission, but I think that here it makes no sense.

Anyway using a library you wouldn't need that, just call the library to get the results.

Hey Gromit.

Thanks for the reply. The "getTFminiData" is something I stole from elsewhere. I have the tfmini library but looking back it IS odd that I also "needed" that getTFminiData function for it to work.

And what about the pin levels? You shouldn't connect them directly. Otherwise if the Arduino sets a pin high at 5V, it can damage the other board.

This was good to know I'll be grabbing a logic level converter asap. Luckily the tfmini is only read. I'm guessing there's no way to make the 2560 drop it's logic level?