ToFFuture XT-S1 Range Finder Distance Measurement

Hello everyone, I am having trouble getting the full range out of the ToFFuture XT-S1 ranging module. I can get to around 12000mm (39ft) without issue, but after that, the sensor jumps to over 65000mm (213ft). The documentation says the sensor should work out to 30m (98ft) which is significantly further than what I am getting. I guess the main thing I am unsure about is if there is anything in my code that is limiting the performance of the sensor.
Thank you in advance for your time!

Further Informaion:
I have tried the sensor both indoors and outdoors with the same results.
I am running the sensor via UART and it is connected to an Arduino Nano.
The output is sent to a 0.96" tft display (ST7735S).
I am unable to upload the product manual PDF for the sensor since I am a new user.

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <SPI.h>
#include <ModbusMaster.h>

// Module connection pins
#define CLK 2
#define DIO 3

// TFT screen connection pins
#define TFT_CS   10
#define TFT_RST   8 // Or set to -1 and connect to Arduino RESET pin
#define TFT_DC    9

#define TFT_MOSI 11  // Data out
#define TFT_SCLK 13  // Clock out

// For ST7735-based displays, we will use this call
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

float p = 3.1415926;

/************************* Kalman Filter *******************************/
bool filters_klm_on = true;
uint16_t klm_factor = 300;
uint16_t klm_threshold = 300;
int last_time = 0;
uint16_t last_distance = 0;

void doKlmFilter(uint16_t & distance)
{
	if(filters_klm_on)
	{			
		bool bfilterstart = false;
		int currentTime = millis();        
		if(abs(currentTime - last_time)<300)//300ms
			bfilterstart = true;
		last_time = currentTime;

		if((distance < 64000)&&(last_distance < 64000)&& bfilterstart)
		{
			int32_t diff = distance - last_distance;

			if (abs(diff) < klm_threshold)
			{
				uint32_t result = 0;
				result = (((distance * klm_factor) + (last_distance * (1000-klm_factor))) / 1000);
				distance = result;
			}
		}			
		last_distance = distance;			
	}
}

/********************************************************/
ModbusMaster master;

void setup() {
  Serial.begin(115200);// Serial Bitrate 115200
  master.begin(1, Serial);// Default Modbus slave id is 1

  tft.initR(INITR_MINI160x80_PLUGIN);  // Init ST7735S mini display

  uint16_t time = millis();
  tft.fillScreen(ST77XX_BLACK);
  time = millis() - time;

  tft.setRotation(3);
  tft.setTextSize(2);
  tft.setCursor(0, 0);
  tft.print("Mini Ranger"); //Product Name
  tft.setCursor(0, 40);
  tft.setTextSize(3);

}

// Initialize the device configuration
void recfgsensor()
{
  // Set the current register to 0
  master.writeSingleRegister(0, 0);
  
  // Set the outgoing mode to 0x0001 for passive reception result mode
  master.writeSingleRegister(61, 0x0001);// Direct reception

  // Set the measurement cycle register to 200ms
  master.writeSingleRegister(66, 200);// Measurement cycle 200ms
  
  // Set the 3rd register to 0x0001: start measuring
  master.writeSingleRegister(3, 0x0001);

  // Set the 3rd register to 0x0002: stop measuring
  // master.writeSingleRegister(3, register set to 0x0002);
}


bool checkConnect()
{  
  if (master.readInputRegisters(22, 4) == master.ku8MBSuccess)// Read 4 registers starting from position 22.
  {
    uint16_t data[4];
    for (int i = 0; i < 4; i++)
    {
      data[i] = master.getResponseBuffer(i);      
    }
    return true;
  }
  return false;
}

uint16_t timeout_count = 0;
bool isConneced = false;


void loop() {

  int k;
  uint8_t data1[] = { 0xff, 0xff, 0xff, 0xff};
  uint8_t blank[] = { 0x00, 0x00, 0x00, 0x00};

  uint8_t result;
  uint8_t data[10];
  int i=0;

  if(isConneced == false)
  {
    if(checkConnect())
    {
      isConneced = true;
      timeout_count = 0;
      recfgsensor();
      Serial.println("connected");
    }
  }else
  {
    if(Serial.available())
    {
      timeout_count = 0;
      delay(1); // Delay 1 ms to wait for a frame of data to be received
      while (Serial.available() > 0) {
        char c = Serial.read();
        if(i<9)
          data[i]= c;
        i++;
      }

      if (i >= 9)// If 9 bytes are received
      {
        uint16_t dist = (data[3]<<8)|data[4];
        Serial.println(dist);
        doKlmFilter(dist);
        if(dist < 64000)
        {        
          // Calculate display value in hundredths of feet
          float displayValue = dist/304.8;  //conversion from mm to ft.

          // Write the value to the tft display.
          tft.setTextColor(ST77XX_WHITE);
          tft.print(displayValue);
          tft.print(" ft");
          delay(2000);

          // Clear the value by overwriting in black and then resetting the cursor.
          tft.setTextColor(ST77XX_BLACK);
          tft.setCursor(0, 40);
          tft.print(displayValue);
          tft.print(" ft");
          tft.setCursor(0, 40);
        }else
        {
          // If the distance is over 64000 mm (209.97ft) display the error message.
          tft.setTextColor(ST77XX_WHITE);
          tft.print("Too Far");
          delay(2000);

          //Clear the error message and reset the cursor position.
          tft.setTextColor(ST77XX_BLACK);
          tft.setCursor(0, 40);
          tft.print("Too Far");
          tft.setCursor(0, 40);
        }
      }

    }else{
      timeout_count ++;
      if(timeout_count > 20)// If no data is received for 2 seconds, it is considered that the device has been disconnected
      {
        timeout_count = 0;
        isConneced = false;        
        Serial.println("Disconnect");
      }
    }
  }

  delay(10); // Delay 0.1s
}

Do you have the limitation if you don't apply your filter?

This looks suspect to me:

...because the math looks like it might be done in uint16_t before being stored in the uint32_t result.

ToFFuture XT-S1 data sheet / manual is here: https://mm.digikey.com/Volume0/opasdata/d220001/medias/docus/6241/XT-S1.pdf

I was thinking the filter might be the issue as well, but I wasn't too sure on what was going on with it. I pulled it from the example that was provided in the documentation. I did however try it with the "bool filters_klm_off = true" set to false and I had the same issues. I also tried commenting out the doKlmFilter(dist) line so that it wouldn't be called in the loop. No success there either.

What does this line print, on either side of the event where you see the jump in measurements?

        uint16_t dist = (data[3]<<8)|data[4];
        Serial.println(dist);

This is what it shows for the Serial.println(dist):
7629
8270
7439
65524
65524
65524
65524
7511
7462

I ran it a few times just now and it is always that 65524 number when the "Too Far" error is triggered.

So is the serial port is connected to both the device that you are reading from and the computer where you are writing to?

If that is so, maybe the shared communication is corrupting the data?

It is connected to both when I am reading those print numbers from the Serial Monitor, but I do have an external power supply that I connect to the Nano in order to test it outdoors. I just tried a direct print of (dist) to the display with the external power supply connected. The same issues happens when I am running off of that too. Same number popping up 65524.

That large constant number could be an error indicator.

On the other hand, this code does not have any means to detect the start and stop of a valid transmitted packet, or check packet integrity, so unreliable communications are a possibility. Well designed sensors have data integrity checks built in to their transmissions.

      while (Serial.available() > 0) {
        char c = Serial.read();
        if(i<9)
          data[i]= c;
        i++;
      }

      if (i >= 9)// If 9 bytes are received
      {
        uint16_t dist = (data[3]<<8)|data[4];
1 Like

I just checked the device manual, and indeed the packet does have data integrity checks:

You can check various things (like whether bytes 0 to 2 are 1,4,4) but one place to start is to look at what is in those presumably faulty packets. Try adding a few lines to show the packet contents, like this:

      if (i >= 9)// If 9 bytes are received
      {
        uint16_t dist = (data[3]<<8)|data[4];
       //add lines here
       if (dist == 65524) {  //dump packet received
       for (int j=0; j<9; j++) {
         Serial.print (data[j],HEX);
         Serial.print(" ");  
        }
     Serial.println(); //end the line
     }
    //end addition
1 Like

Thank you for all of the quick replies. I can't mess with it today since I am heading out of town, but that does look promising. I will post an update as soon as I can!