TF Luna Lidar blocking serial line, freezing processor

So, when I try using the TF Luna Lidar, the Nano frequently stops at the point where it is attempting the read using I2C. The code freezes at the read, which I can verify by both missing subsequent serial messages and missing subsequent blinking of the LED. After this happens, I am unable to load a new sketch due to "Serial port is busy" - I assume that means it is actually unresponsive, but am not positive. After a hard reset, everything is fine. In some cases, I am able to read and process the TF Luna data normally, so the wiring is correct. If I purposely unplug the TF Luna, I get the same frozen issue. It is not the connections; they've been both soldered and breadboarded at various times. It probably is timing related as adding delays seems to improve results.

I am using a Nano BLE Sense rev 2 although have the same issue on an RP2040.
I have two TF Lunas, both exhibit the same behavior. Libraries are in the code below.

Why is the process hanging?


#include <Nano33BLE_System.h>
#include <Arduino_BMI270_BMM150.h> // library for Nano BLE rev 2 IMU
#include <Wire.h>    // Instantiate the Wire library
#include <TFLI2C.h>  // TFLuna-I2C Library v.0.1.1


TFLI2C tflI2C;
// Lidar parameters

int16_t tfDist;                // distance in centimeters
float tfDistMeterwater;        // distance in meters of seawater
int16_t tfAddr = TFL_DEF_ADR;  // Use this default I2C address
float index_refract = 1.335;   //

// IMU parameters

float Ax, Ay, Az;
float Mx, My, Mz;
float Anormal, Atheta, Aphi;  // setting up for accel vector in spherical coordinate system
float Mnormal, Mtheta, Mphi;  // setting up for magnetic vector in spherical coordinate system

void setup() {
  Serial.begin(9600);
  pinMode(LED_BUILTIN, OUTPUT);
  while (!Serial);
  if (!IMU.begin()) {
    Serial.println("Failed to initialize IMU!");
  }
}

void loop() {

  Serial.println("Start of Loop");
  if (tflI2C.getData(tfDist, tfAddr)) {
      Serial.println("Will not make it here");
      tfDistMeterwater = index_refract * (tfDist + 0.0) / 100.0;
      Serial.print("Distance:");
      Serial.print('\t');
      Serial.println(tfDistMeterwater);  
  }
  // Blink the LED to see if the serial port is frozen or the whole processor is
  
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  delay(1000);                      // wait for a second
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
   
  delay(2500);

  if (IMU.accelerationAvailable()) {
    IMU.readAcceleration(Ax, Ay, Az);  //output in G's

    // axis definition - different from some Arduino documents
    // definition +X is long axis of board, USB port is +X side
    // definition +Y is width of board, power light is +Y side
    // definition +Z is up from plane of board, bottom side of board is +Z (down)

    Anormal = sqrt((Ax * Ax) + (Ay * Ay) + (Az * Az));  // normalize for coordinate system transformation
    Atheta = (180 / 3.14159) * acos(Az / Anormal);      // using physics and not mathematics convention
    Aphi = (180 / 3.14159) * atan2(Ay, Ax);             //+X axis is zero, positive toward +Y, negative toward -Y


    Serial.print("Accelerometer data: ");
    Serial.print('\t');
    Serial.print(Ax);
    Serial.print('\t');
    Serial.print(Ay);
    Serial.print('\t');
    Serial.print(Az);
    Serial.print('\t');
    Serial.print(Atheta);
    Serial.print('\t');
    Serial.println(Aphi);
    Serial.println();
  }

  IMU.readMagneticField(Mx, My, Mz);
  //
  // axis definition - different from some Arduino documents
  // definition X is across width of board, power LED is +X side
  // definition Y is out of board plane, up is +Y side
  // definition Z is long axis of board, USB port is -Z side

  Mnormal = sqrt((Mx * Mx) + (My * My) + (Mz * Mz));  // normalize for coordinate system transformation
  Mtheta = (180 / 3.14159) * acos(Mx / Mnormal);      // using physics and not mathematics convention
  Mphi = 180 + ((180 / 3.14159) * atan2(My, -Mz));    // in normal compass coordinate system

  Serial.print("Magnetometer data: ");
  Serial.print(Mx);
  Serial.print('\t');
  Serial.print(My);
  Serial.print('\t');
  Serial.println(Mz);
  Serial.print('\t');
  Serial.println(Mtheta);  //dip
  Serial.print('\t');
  Serial.println(Mphi);
  Serial.print('\t');
  Serial.println(Mnormal);

  if (Mphi <= 20 || Mphi >= 340) {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  if (Mphi <= 200 || Mphi >= 160) {
    digitalWrite(LED_BUILTIN, LOW);
  }
}

But not always?

Often this is caused by RFI (Radio Frequency Interference) from other things like motors, or the way the circuit is constructed. Solderless bread board often has the habit of looking like things are connected together when they are not.

Also it could be that your TF Luna Lidar itself is generating this interference because the circuit is constructed without any power supply decoupling capacitors. See this tutorial about this problem.

Thanks for the reply, @Grumpy_Mike. Yes, I have 0.1 uF decoupling capacitor near the power supply leads and, other than the Arduino and the TF Luna, there is no other connected or especially nearby hardware. By saying "frequently", I didn't mean to imply it was acting flaky and intermittent as from EMI or a poor connection. I meant that it depends on the timing. I have other sketches with different timing where everything works. There is something about the communication timing that matters. I think that the Wire.h code does not return - no watchdog? But I'm confused as to why the processor seems to lock up on I2C traffic in the first place. I've run the serial port at 115,200 and 9,600 to ease the loading, but it doesn't help. Not much else is going on in that code - it doesn't make it to the magnetometer or accelerometer activity.

I don't understand what you mean by this, can you please explain.

The thing is that the speed of I2C communications is not affected by any delays in your code. It is possible to run I2C communications at other faster speeds but not all I2C devices are capable of acting at higher speeds.

There is no watchdog on I2C. If you ask the I2C device for more bytes than it actually supplies will result in it appearing to hang. so check what you will actually get back from your I2C device.

OK, for anyone who stumbles across this, there were multiple things going on:

  1. A coding error on my part that masked some of the real issues.

  2. The Tf Luna whether rigged as I2C or UART needs a wait added after the read before going on to other reads or (maybe) other bus traffic. It seems a bit variable, but 15 msec after every read is enough to work consistently for me.

  3. The Wire.h that is the default on my Nano BLE Sense rev 2 IDE installation does not have a watchdog (timeout) capability. Hunt around and install one of the newer versions that do (Christoforo made the relevant update). Knowing that would have saved me many hours of fighting to get the Arduino unstuck. This is a case where the reset and double reset (to bootloader) do not clear the error. Nor does power cycling the Arduino - it just re-runs the faulty code and gets trapped again. You'll need to:

  4. Ground the reset pin through a button or switch.

  5. Load a different sketch like 'blink', while keeping the board in reset.

  6. At about the moment that the IDE switches to "uploading....", release the reset of the board with your switch.

  7. Repeat above until you get the sketch loaded and learn the exact timing to make it easier the next time.