/* 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 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,0);

    /* 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(30);
		FinalAddress = 0x52+(i+1)*2;

	   APP_LOG(TS_OFF, VLEVEL_L, "\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_L, "#%u read id = %04x I2C ADDR=0x%2X\r\n",i, Id, pDev->I2cDevAddr);
		   if (status) {
				 APP_LOG(TS_OFF, VLEVEL_L, "# Read id fail\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_L, "#i VL53L0X_SetDeviceAddress fail\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);
			   status = VL53L0X_DataInit(pDev);
			   if( status == 0 ){
				   pDev->Present = 1;
				   APP_LOG(TS_OFF, VLEVEL_L, "#%u VL53L0X_SetDeviceAddress to 0x%02x\r\n",i, pDev->I2cDevAddr);

				   nDevPresent++;
				   nDevMask |= 1 << i;
				   pDev->Present = 1;
				   APP_LOG(TS_OFF, VLEVEL_L, "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_L, "VL53L0X_DataInit %d fail\n");
				   break;
			   }

		   } else {
			   APP_LOG(TS_OFF, VLEVEL_L, "# unknown ID %x\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_L, "\r\n ######  Starting Range   #%u sensor \r\n",i);

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

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

		  status = VL53L0X_PerformRefSpadManagement(&VL53L0XDevs[i], &refSpadCount, &isApertureSpads);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_L, "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_L, "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_L, "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_L, "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_L, "Not Supported");
			  break;
		  }

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

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

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

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

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

		  status = VL53L0X_PerformRefCalibration(&VL53L0XDevs[i], &VhvSettings, &PhaseCal);
		  if( status ){
			  APP_LOG(TS_OFF, VLEVEL_L, "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_TOF_VL53L0X_Range_Process(void)
{
  int status=0, i;
  RangingConfig_e RangingConfig = HIGH_ACCURACY; //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_L, "\r\n %u pcs sensor(s) online \r\n", nSensorPresent);
  }

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

	  for (i=0; i< MAX_TOF_COUNT; i++)
	  {
		  if (VL53L0XDevs[i].Present ==1)
		  {
			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);
			  	 }
			  	// ##########     two conditions
			  	// ##########     1) return status ==0,
			  	// ##########     2) and ranging status for valid ranging value !!!!!!!!!!!!!!!!!
			}
		  }

		  HAL_Delay(30);
	  }  // for i < MAX_TOF_COUNT

	  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
}



/* USER CODE BEGIN 4 */

/* USER CODE END 4 */


/**
  * @}
  */ 

/**
  * @}
*/ 

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