/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    app_tof_vl53l0x_range.c
  * @author  Yunhorn (r) Technology Limited Application Team
  * @brief   Application of the LRWAN Middleware
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 Yunhorn Technology Limited.
  * Copyright (c) 2023 Shenzhen Yunhorn Technology Co., Ltd.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "stm32xxx_hal.h"

/* USER CODE BEGIN Includes */
#include <string.h>
#include "X-WL55_WLE5_53L0X.h"
#include "main.h"
#include "vl53l0x_api.h"
#include <limits.h>
#include "sys_app.h"

/**
 * @defgroup Configuration Static configuration
 * @{
 */

/** @}  */ /* config group */

#ifndef MIN
#   define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

/**
 * @defgroup ErrCode Errors code shown on display
 * @{
 */
#define		STS_MAX_L0_RANGE		((uint32_t) 2500)
#define ERR_DETECT             -1
#define ERR_DEMO_RANGE_ONE     1
#define ERR_DEMO_RANGE_MULTI   2
extern I2C_HandleTypeDef hi2c2;
#define X_WL55_WLE5_53L0X_hi2c hi2c2

/** }@} */ /* defgroup ErrCode */


/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

typedef enum {
	LONG_RANGE 		= 0, /*!< Long range mode */
	HIGH_SPEED 		= 1, /*!< High speed mode */
	HIGH_ACCURACY	= 2, /*!< High accuracy mode */
} RangingConfig_e;

/**
 * Global ranging struct
 */
VL53L0X_RangingMeasurementData_t RangingMeasurementData;

/** leaky factor for filtered range
 *
 * r(n) = averaged_r(n-1)*leaky +r(n)(1-leaky)
 *
 * */
int LeakyFactorFix8 = (int)( 0.6 *256);				// 0.6 for ranging, 0.0 for gesture detect
/** How many device detect set by @a DetectSensors()*/
int nDevPresent=0;
/** bit is index in VL53L0XDevs that is not necessary the dev id of the BSP */
int nDevMask=0;

int nSensorPresent;

volatile int sts_tof_distance_data[MAX_TOF_COUNT]={0,0,0};
extern volatile uint8_t sensor_data_ready;

VL53L0X_Dev_t VL53L0XDevs[]={
		{.Id=XNUCLEO53L0X_LEFT, .DevLetter='l',.I2cHandle=&X_WL55_WLE5_53L0X_hi2c, .I2cDevAddr=0x52},
		{.Id=XNUCLEO53L0X_CENTER,.DevLetter='c', .I2cHandle=&X_WL55_WLE5_53L0X_hi2c, .I2cDevAddr=0x52},
		{.Id=XNUCLEO53L0X_RIGHT, .DevLetter='r',.I2cHandle=&X_WL55_WLE5_53L0X_hi2c, .I2cDevAddr=0x52},
};

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
//void ResetAndDetectSensor(int SetDisplay);

void sts_tof_vl53l0x_Sensor_SetNewRange(VL53L0X_Dev_t *pDev, VL53L0X_RangingMeasurementData_t *pRange);
void sts_tof_vl53l0x_SetupSingleShot(RangingConfig_e rangingConfig);
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */

/**
 * Handle Error
 *
 * Set err on display and loop forever
 * @param err Error case code
 */
void HandleError(int err){
    //char msg[16];
    //sprintf(msg,"Er%d", err);
    APP_LOG(TS_OFF, VLEVEL_L,"Er %04u\r\n",err);
    //while(1){};
}



/**
 * Reset all sensor then goes true presence detection
 *
 * All present devices are data initiated and assigned with their final address
 * @return
 */
int sts_tof_vl53l0x_DetectSensors(void)
{
	int i;
    uint16_t Id;
    int status;
    int FinalAddress;
    nDevPresent = 0;

    for (i=0; i < MAX_TOF_COUNT; i++) {
    	XWL55_WLE5_53L0X_ResetId(i,0);
    	//XWL55_WLE5_53L0X_SetIntrStateId(0,i);
    }

    /* detect all sensors (even on-board)*/
    for (i=0; i < MAX_TOF_COUNT; i++)
    {
		VL53L0X_Dev_t *pDev;
		pDev = &VL53L0XDevs[i];
		pDev->I2cDevAddr = 0x52;
		pDev->Present = 0;
		XWL55_WLE5_53L0X_ResetId(pDev->Id, 1);
		HAL_Delay(3);
		FinalAddress = 0x52+(i+1)*2;

	   //APP_LOG(TS_OFF, VLEVEL_H, "\r\n Detect TOF sensors #%u with I2CDevAddr=0x%02x \r\n", i, pDev->I2cDevAddr);

	   do {
		   /* Set I2C standard mode (400 KHz) before doing the first register access */
		   //if (status == VL53L0X_ERROR_NONE)
		   status = VL53L0X_WrByte(pDev, 0x88, 0x00);

		   /* Try to read one register using default 0x52 address */
		   status = VL53L0X_RdWord(pDev, VL53L0X_REG_IDENTIFICATION_MODEL_ID, &Id);
		   //APP_LOG(TS_OFF, VLEVEL_H, "#%u read id = %04x I2C ADDR=0x%2X\r\n",i, Id, pDev->I2cDevAddr);
		   if (status) {
				 APP_LOG(TS_OFF, VLEVEL_H, "# Read id fail \r\n");
				 break;
		   }
		   if (Id == 0xEEAA) {
			   /* Sensor is found => Change its I2C address to final one */
			   status = VL53L0X_SetDeviceAddress(pDev,FinalAddress);
			   if (status != 0) {
				   APP_LOG(TS_OFF, VLEVEL_H, "#i VL53L0X_SetDeviceAddress fail\r\n");
				   break;
			   }
			   pDev->I2cDevAddr = FinalAddress;
			   /* Check all is OK with the new I2C address and initialize the sensor */
			   status = VL53L0X_RdWord(pDev, VL53L0X_REG_IDENTIFICATION_MODEL_ID, &Id);
			   if (status !=0)
			   {
				   APP_LOG(TS_OFF, VLEVEL_H, "#i VL53L0X_RdWord fail\r\n");
				   break;
			   }
			   status = VL53L0X_DataInit(pDev);
			   if( status == 0 ){
				   pDev->Present = 1;
			   }
			   else{
				   APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_DataInit %d fail\r\n", Id);
				   break;
			   }
			   nDevPresent++;
			   nDevMask |= 1 << i;
			   pDev->Present = 1;
			   APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X %d Present and initiated to final 0x%2x,  Position Mask=0x%02x\r\n", pDev->Id, pDev->I2cDevAddr, nDevMask);

		   } else {
			   APP_LOG(TS_OFF, VLEVEL_H, "# unknown ID %x\r\n", Id);
			   status = 1;
		   }
	   } while(0);
		/* Display detected sensor(s) */
	   if (status) {
		   XWL55_WLE5_53L0X_ResetId(i,0);
	   }
	}

    return nDevPresent;
}

/**
 *  Setup all detected sensors for single shot mode and setup ranging configuration
 */
void sts_tof_vl53l0x_SetupSingleShot(RangingConfig_e rangingConfig)
{	int i;
    int status=VL53L0X_ERROR_NONE;
    uint8_t VhvSettings;
    uint8_t PhaseCal;
    uint32_t refSpadCount;
	uint8_t isApertureSpads;
	FixPoint1616_t signalLimit = (FixPoint1616_t)(0.25*65536);
	FixPoint1616_t sigmaLimit = (FixPoint1616_t)(18*65536);
	uint32_t timingBudget = 33000;
	uint8_t preRangeVcselPeriod = 14;
	uint8_t finalRangeVcselPeriod = 10;
    // uart_printf("\r\n#########  start setup for single shot \r\n");
	for( i=0; i< MAX_TOF_COUNT; i++){

		if( VL53L0XDevs[i].Present){

		  APP_LOG(TS_OFF, VLEVEL_H, "\r\n ######  Starting Range   #%u sensor \r\n",i);

		  status=VL53L0X_StaticInit(&VL53L0XDevs[i]);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_StaticInit failed\n");
		  }

		  status = VL53L0X_PerformRefCalibration(&VL53L0XDevs[i], &VhvSettings, &PhaseCal);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_PerformRefCalibration failed\n");
		  }

		  status = VL53L0X_PerformRefSpadManagement(&VL53L0XDevs[i], &refSpadCount, &isApertureSpads);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_PerformRefSpadManagement failed\n");
		  }

		  status = VL53L0X_SetDeviceMode(&VL53L0XDevs[i], VL53L0X_DEVICEMODE_SINGLE_RANGING); // Setup in single ranging mode
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetDeviceMode failed\n");
		  }
		  status = VL53L0X_SetLimitCheckEnable(&VL53L0XDevs[i], VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, 1); // Enable Sigma limit
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetLimitCheckEnable failed\n");
		  }
		  status = VL53L0X_SetLimitCheckEnable(&VL53L0XDevs[i], VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, 1); // Enable Signa limit
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetLimitCheckEnable failed\n");
		  }
		  /* Ranging configuration */
		  switch(rangingConfig) {
			  case LONG_RANGE:
				  signalLimit = (FixPoint1616_t)(0.1*65536);
				  sigmaLimit = (FixPoint1616_t)(60*65536);
				  timingBudget = 33000;
				  preRangeVcselPeriod = 18;
				  finalRangeVcselPeriod = 14;
			  break;
			  case HIGH_ACCURACY:
				  signalLimit = (FixPoint1616_t)(0.25*65536);
				  sigmaLimit = (FixPoint1616_t)(18*65536);
				  timingBudget = 200000;
				  preRangeVcselPeriod = 14;
				  finalRangeVcselPeriod = 10;
			  break;
			  case HIGH_SPEED:
				  signalLimit = (FixPoint1616_t)(0.25*65536);
				  sigmaLimit = (FixPoint1616_t)(32*65536);
				  timingBudget = 20000;
				  preRangeVcselPeriod = 14;
				  finalRangeVcselPeriod = 10;
			  break;
			  default:
				  APP_LOG(TS_OFF, VLEVEL_H, "Not Supported");
			  break;
		  }

		  status = VL53L0X_SetLimitCheckValue(&VL53L0XDevs[i],  VL53L0X_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, signalLimit);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetLimitCheckValue failed\n");
		  }

		  status = VL53L0X_SetLimitCheckValue(&VL53L0XDevs[i],  VL53L0X_CHECKENABLE_SIGMA_FINAL_RANGE, sigmaLimit);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetLimitCheckValue failed\n");
		  }

		  status = VL53L0X_SetMeasurementTimingBudgetMicroSeconds(&VL53L0XDevs[i],  timingBudget);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetMeasurementTimingBudgetMicroSeconds failed\n");
		  }

		  status = VL53L0X_SetVcselPulsePeriod(&VL53L0XDevs[i],  VL53L0X_VCSEL_PERIOD_PRE_RANGE, preRangeVcselPeriod);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetVcselPulsePeriod failed\n");
		  }

		  status = VL53L0X_SetVcselPulsePeriod(&VL53L0XDevs[i],  VL53L0X_VCSEL_PERIOD_FINAL_RANGE, finalRangeVcselPeriod);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_SetVcselPulsePeriod failed\n");
		  }

		  status = VL53L0X_PerformRefCalibration(&VL53L0XDevs[i], &VhvSettings, &PhaseCal);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_H, "VL53L0X_PerformRefCalibration failed\n");
		  }

		  VL53L0XDevs[i].LeakyFirst =1;

		}
	}
  
}


/* Store new ranging data into the device structure, apply leaky integrator if needed */
void sts_tof_vl53l0x_Sensor_SetNewRange(VL53L0X_Dev_t *pDev, VL53L0X_RangingMeasurementData_t *pRange)
{
    if( pRange->RangeStatus == 0 ){
        if( pDev->LeakyFirst ){
            pDev->LeakyFirst = 0;
            pDev->LeakyRange = pRange->RangeMilliMeter;
        }
        else{
            pDev->LeakyRange = (pDev->LeakyRange*LeakyFactorFix8 + (256-LeakyFactorFix8)*pRange->RangeMilliMeter)>>8;
        }
    }
    else{
        pDev->LeakyFirst = 1;
    }
}

/* USER CODE END 0 */
void STS_TOF250_Range_Process(void)
{
#define I2C_TIME_OUT_BASE   10
#define I2C_TIME_OUT_BYTE   1
	int status;
	uint8_t pdata[2]={0x0,0x0}; uint32_t count=2;
	int i2c_time_out = I2C_TIME_OUT_BASE + count*I2C_TIME_OUT_BYTE;

	sensor_data_ready = 0 ;
    sts_tof_distance_data[0] = STS_MAX_L0_RANGE;
	sts_tof_distance_data[1] = STS_MAX_L0_RANGE;
	sts_tof_distance_data[2] = STS_MAX_L0_RANGE;

	status = HAL_I2C_Master_Receive(&hi2c2, TOF250_I2C_ADDR, pdata, count, i2c_time_out);
	if (status == HAL_OK) {
		APP_LOG(TS_OFF, VLEVEL_H, "\r\n## TOF250 ranging value %02x %02x %4d ",pdata[0],pdata[1], *pdata);
		sts_tof_distance_data[2] = pdata[0]*255+pdata[1];
		sensor_data_ready = 1;
		APP_LOG(TS_OFF, VLEVEL_H, "\r\n## Measured Range: \r\nTOF #0 = %4u mm, \r\nTOF #1 = %4u mm, \r\nTOF #2 = %4u mm\r\n",
				  (int)sts_tof_distance_data[0],(int)sts_tof_distance_data[1],(int)sts_tof_distance_data[2]);
	  }
}
void STS_TOF_VL53L0X_Range_Process(void)
{
  int status=0, i;
  RangingConfig_e RangingConfig = LONG_RANGE; //HIGH_ACCURACY;		//LONG_RANGE;
  XWL55_WLE5_53L0X_Init();
  if ((nDevMask ==0) || (nSensorPresent ==0))
  {
	  nSensorPresent = sts_tof_vl53l0x_DetectSensors();					// confirm sensor online
	  APP_LOG(TS_OFF, VLEVEL_H, "\r\n %u pcs sensor(s) online \r\n", nSensorPresent);
  }
  sts_tof_distance_data[0] = 0;
  sts_tof_distance_data[1] = 0;
  sts_tof_distance_data[2] = 0;

  if (nSensorPresent > 0)
  {
	  // RangingConfig == HIGH ACCURACY, LONG RANGE, HIGH SPEED
	  sts_tof_vl53l0x_SetupSingleShot(RangingConfig);

	  sensor_data_ready = 0 ;
	  for (i=0; i< MAX_TOF_COUNT; i++)
	  {
		  if (VL53L0XDevs[i].Present ==1)
		  {
			uint8_t rep=0;
			do {
				rep++;
				status = VL53L0X_PerformSingleRangingMeasurement(&VL53L0XDevs[i], &RangingMeasurementData);
				if (status ==0)
				{
					sts_tof_vl53l0x_Sensor_SetNewRange(&VL53L0XDevs[i],&RangingMeasurementData);
					if( RangingMeasurementData.RangeStatus == 0 )
					{
						sts_tof_distance_data[i] = (int)VL53L0XDevs[i].LeakyRange;

						nDevMask |= (1 << i);
						sensor_data_ready |= 1;
					}  else
					{
						//HandleError(ERR_DEMO_RANGE_ONE);
						APP_LOG(TS_OFF, VLEVEL_M,"\r\n#%u sensor   ERR code = %04u\r\n",i, ERR_DEMO_RANGE_ONE);
					}
						// ##########     two conditions
						// ##########     1) return status ==0,
						// ##########     2) and ranging status for valid ranging value !!!!!!!!!!!!!!!!!
					}
					HAL_Delay(1);
				} while ((RangingMeasurementData.RangeStatus != 0)&&(rep <10));
				sts_tof_distance_data[i] = (int)VL53L0XDevs[i].LeakyRange ==0?STS_MAX_L0_RANGE:(int)VL53L0XDevs[i].LeakyRange;
		  }

		  HAL_Delay(5);
	  }  // for i < MAX_TOF_COUNT
	  if (sensor_data_ready != 0) {
		  APP_LOG(TS_OFF, VLEVEL_L, "\r\n## Measured Range: \r\nTOF #0 = %4u mm, \r\nTOF #1 = %4u mm, \r\nTOF #2 = %4u mm\r\n",
				  (int)sts_tof_distance_data[0],(int)sts_tof_distance_data[1],(int)sts_tof_distance_data[2]);
	  }

  }   // nSensorPresent >0

  // reset for next ranging
  //nDevMask = 0;
  //nSensorPresent = 0;
}



/* USER CODE BEGIN 4 */

/* USER CODE END 4 */


/**
  * @}
  */ 

/**
  * @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/