/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    lora_app.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.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "platform.h"
#include "sys_app.h"
#include "lora_app.h"
#include "stm32_seq.h"
#include "stm32_timer.h"
#include "utilities_def.h"
#include "app_version.h"
#include "lorawan_version.h"
#include "subghz_phy_version.h"
#include "lora_info.h"
#include "LmHandler.h"
#include "adc_if.h"
#include "CayenneLpp.h"
#include "sys_sensors.h"
#include "flash_if.h"

/* USER CODE BEGIN Includes */
#include "app_tof_pin_conf.h"
#include "app_tof.h"
#include "yunhorn_sts_sensors.h"
#include "yunhorn_sts_prd_conf.h"
#include "sts_cmox_hmac_sha.h"
#include "X-WL55_WLE5_53L0X.h"
/* USER CODE END Includes */

/* External variables ---------------------------------------------------------*/
/* USER CODE BEGIN EV */
extern volatile uint8_t sts_service_mask;
extern volatile uint32_t rfac_timer;
volatile uint8_t sts_ac_code[YUNHORN_STS_AC_CODE_SIZE]={0x0};
extern hmac_result_t hmac_result;
volatile uint8_t sts_work_mode =0;
volatile uint8_t sensor_data_ready=0;
extern volatile uint8_t sts_soap_level_state;
extern volatile uint8_t ToF_EventDetected;
extern volatile int sts_tof_distance_data[MAX_TOF_COUNT];
volatile uint32_t SamplingPeriodicity = 1000; 		//unit ms
volatile uint32_t HeartBeatPeriodicity = 120000; 		//unit ms
volatile uint8_t STS_LoRa_WAN_Joined = 0;

volatile uint8_t heart_beat_timer =0;
volatile uint8_t outbuf[128]={0x0};
volatile uint8_t upload_message_timer =0;
volatile sts_cfg_nvm_t sts_cfg_nvm = {
		sts_mtmcode1,
		sts_mtmcode2,
		sts_version,
		sts_hardware_ver,
		0x01,		//Regular TxPeriodicity interval
		'M', 		//Uplink data interval unit,  for heart-beat uplink
		0x3C,		//Heart-beat interval or Sampling interval
		'M', 		//Sampling sensor interval unit, for real-time sensing of MEMS
		0x04,		// dual mode=4, uni_mode =5
		0x00,		// sts service mask
		0x00, 		//sts_ioc_mask
		0x20,   	//32 bytes, below start of p[0] 20 BYTES AND 12 BYTES FALL DOWN CFG
		{			// below 20 bytes
		0x08,		//start_m [8]*0.1 meter =0.8
		0x19,		//lenght_m 0x19=[25]*0.1=2.5f meter
		0x0F,		//threshold  0X0F=[15]*0.1=1.5f
		0x28,		//receiver gain 0x28 =[40]*0.01=0.40f max 99=0x63
		0x04,		//profile [4]=4
		0x0A,		//rate tracking 0x0A=10= 10U
		0x41,		//rate presence 0x41=65= 65U
		0x3F,		//hwaas   0x3F=63 =63U
		0x00,		//nbr removed pc [0]=0
		0x05,		//inter frame deviation time const 0x05=[5]*0.1=0.5f
		0x0A,		//inter frame fast cutoff 0x0A=[10] = 10U
		0x01,		//inter frame slow cutoff,0x01=1[1]*0.01=0.01f
		0x00,		///intra frame time const [0]=0   Lower to reduce sensitivity, higher to increase sensitivity
		0x0A,		//intra frame weight, 0x00=[0]*0.1=0.0F		0x0A=10, 10*0.1=1 FOR FAST MOVEMENT TRACKING FALL DETECTION
		0x05,		//output time const 0x05=[5]*0.1=0.5
		0x02,		//downsampling factor [2]=2
		0x03,		//power saving mode ACTIVE [3] = 3U
		0x00,		//reserve --P[17]
		0x00,		//reserve --P[18]
		0x00,		//reserve --P[19]
		},			// above  20 bytes
		0x00,		//reserve2
		0x00,		//reserve3
		0x20,		//sensor install height in 10 cm, default 32*10=320cm, 3.2meter

		0x00,		//reserve5	alarm_parameter05
		0x06,		//reserve6  alarm_mute_or_reset_expire_timer_in_10sec, 60 seconds
		0x23,		//reserve7  alarm Lamp Bar Flashing color define, 0x20, 2==STS_RED, 0 = STS_DARK, 0x23, 2=STS_RED, 3=STS_BLUE
		0x03, 		//reserve8  occupancy over time threshold  3*10 = 30 minutes

		0x09,		//reserve8  motionless_duration_threshold_in_min+1, normal: 10 min(0x0A) Minutes (2 min.) 1-9== 2-10min
		0x09, 		//unconscious threshold * 128, 0-9, 9*128=1280 motion level

		0x01,		//fall_detection_acc_threshold = *10 acceleration measure
		0x03,		//fall detection_depth_threshold  *10cm
		0x03,		//falldown_confirm_threshold_in_10sec, 0x3=30 sec default
					// below 20 bytes for RFAC code
		{0x0,0x0,0x0,0x0,0x0,   0x0,0x0,0x0,0x0,0x0,    0x0,0x0,0x0,0x0,0x0,   0x0,0x0,0x0,0x0,0x0},
};


/* USER CODE END EV */

/* Private typedef -----------------------------------------------------------*/
/**
  * @brief LoRa State Machine states
  */
typedef enum TxEventType_e
{
  /**
    * @brief Appdata Transmission issue based on timer every TxDutyCycleTime
    */
  TX_ON_TIMER,
  /**
    * @brief Appdata Transmission external event plugged on OnSendEvent( )
    */
  TX_ON_EVENT
  /* USER CODE BEGIN TxEventType_t */

  /* USER CODE END TxEventType_t */
} TxEventType_t;

/* USER CODE BEGIN PTD */
//#define YUNHORN_STS_SAMPLING_CHECK_TIME			SamplingPeriodicity
//#define YUNHORN_STS_UPLOADING_MESSAGE_TIME		UploadingMessagePeriodicity
//#define YUNHORN_STS_HEART_BEAT_CHECK_TIME			HeartBeatPeriodicity
#define YUNHORN_STS_RSS_WAKEUP_CHECK_TIME 		SamplingPeriodicity		//3000 ms
#define YUNHORN_STS_SAMPLING_CHECK_TIME			SamplingPeriodicity
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/**
  * LEDs period value of the timer in ms
  */
#define LED_PERIOD_TIME 500

/**
  * Join switch period value of the timer in ms
  */
#define JOIN_TIME 2000

/*---------------------------------------------------------------------------*/
/*                             LoRaWAN NVM configuration                     */
/*---------------------------------------------------------------------------*/
/**
  * @brief LoRaWAN NVM Flash address
  * @note last 2 sector of a 128kBytes device
  */
#define LORAWAN_NVM_BASE_ADDRESS                    ((void *)0x0803F000UL)

/* USER CODE BEGIN PD */
static const char *slotStrings[] = { "1", "2", "C", "C_MC", "P", "P_MC" };
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private function prototypes -----------------------------------------------*/
/**
  * @brief  LoRa End Node send request
  */
static void SendTxData(void);

/**
  * @brief  TX timer callback function
  * @param  context ptr of timer context
  */
static void OnTxTimerEvent(void *context);

/**
  * @brief  join event callback function
  * @param  joinParams status of join
  */
static void OnJoinRequest(LmHandlerJoinParams_t *joinParams);

/**
  * @brief callback when LoRaWAN application has sent a frame
  * @brief  tx event callback function
  * @param  params status of last Tx
  */
static void OnTxData(LmHandlerTxParams_t *params);

/**
  * @brief callback when LoRaWAN application has received a frame
  * @param appData data received in the last Rx
  * @param params status of last Rx
  */
static void OnRxData(LmHandlerAppData_t *appData, LmHandlerRxParams_t *params);

/**
  * @brief callback when LoRaWAN Beacon status is updated
  * @param params status of Last Beacon
  */
static void OnBeaconStatusChange(LmHandlerBeaconParams_t *params);

/**
  * @brief callback when system time has been updated
  */
static void OnSysTimeUpdate(void);

/**
  * @brief callback when LoRaWAN application Class is changed
  * @param deviceClass new class
  */
static void OnClassChange(DeviceClass_t deviceClass);

/**
  * @brief  LoRa store context in Non Volatile Memory
  */
static void StoreContext(void);

/**
  * @brief  stop current LoRa execution to switch into non default Activation mode
  */
static void StopJoin(void);

/**
  * @brief  Join switch timer callback function
  * @param  context ptr of Join switch context
  */
static void OnStopJoinTimerEvent(void *context);

/**
  * @brief  Notifies the upper layer that the NVM context has changed
  * @param  state Indicates if we are storing (true) or restoring (false) the NVM context
  */
static void OnNvmDataChange(LmHandlerNvmContextStates_t state);

/**
  * @brief  Store the NVM Data context to the Flash
  * @param  nvm ptr on nvm structure
  * @param  nvm_size number of data bytes which were stored
  */
static void OnStoreContextRequest(void *nvm, uint32_t nvm_size);

/**
  * @brief  Restore the NVM Data context from the Flash
  * @param  nvm ptr on nvm structure
  * @param  nvm_size number of data bytes which were restored
  */
static void OnRestoreContextRequest(void *nvm, uint32_t nvm_size);

/**
  * Will be called each time a Radio IRQ is handled by the MAC layer
  *
  */
static void OnMacProcessNotify(void);

/**
  * @brief Change the periodicity of the uplink frames
  * @param periodicity uplink frames period in ms
  * @note Compliance test protocol callbacks
  */
static void OnTxPeriodicityChanged(uint32_t periodicity);

/**
  * @brief Change the confirmation control of the uplink frames
  * @param isTxConfirmed Indicates if the uplink requires an acknowledgement
  * @note Compliance test protocol callbacks
  */
static void OnTxFrameCtrlChanged(LmHandlerMsgTypes_t isTxConfirmed);

/**
  * @brief Change the periodicity of the ping slot frames
  * @param pingSlotPeriodicity ping slot frames period in ms
  * @note Compliance test protocol callbacks
  */
static void OnPingSlotPeriodicityChanged(uint8_t pingSlotPeriodicity);

/**
  * @brief Will be called to reset the system
  * @note Compliance test protocol callbacks
  */
static void OnSystemReset(void);

/* USER CODE BEGIN PFP */

/**
  * @brief  LED Tx timer callback function
  * @param  context ptr of LED context
  */
static void OnTxTimerLedEvent(void *context);

/**
  * @brief  LED Rx timer callback function
  * @param  context ptr of LED context
  */
static void OnRxTimerLedEvent(void *context);

/**
  * @brief  LED Join timer callback function
  * @param  context ptr of LED context
  */
static void OnJoinTimerLedEvent(void *context);

/**
  * @brief  Yunhorn STS Occupancy RSS WakeUP timer callback function
  * @param  context ptr of STS RSS WakeUp context
  */
//static void OnYunhornSTSOORSSWakeUpTimerEvent(void *context);

/**
  * @brief  Yunhorn STS Heart Beat Periodicity Chagne function
  * @param  duration of periodicty in ms (1/1000 sec)
  */
static void OnYunhornSTSHeartBeatPeriodicityChanged(uint32_t periodicity);

/**
  * @brief  Yunhorn STS Heart Beat callback function
  * @param  context ptr of STS Sampling Check context
  */
static void OnYunhornSTSHeartBeatTimerEvent(void *context);


/**
  * @brief  Yunhorn STS Uploading Message periodically
  * @param  context ptr of context
  */
//static void OnYunhornSTSUploadingMessageEvent(void *context);

/**
  * @brief  Yunhorn RFAC Handle process
  * @param  void
  */
static void STS_YUNHORN_RFAC_HANDLE_PROCESS(void);

/* USER CODE END PFP */

/* Private variables ---------------------------------------------------------*/
/**
  * @brief LoRaWAN default activation type
  */
static ActivationType_t ActivationType = LORAWAN_DEFAULT_ACTIVATION_TYPE;

/**
  * @brief LoRaWAN force rejoin even if the NVM context is restored
  */
static bool ForceRejoin = LORAWAN_FORCE_REJOIN_AT_BOOT;

/**
  * @brief LoRaWAN handler Callbacks
  */
static LmHandlerCallbacks_t LmHandlerCallbacks =
{
  .GetBatteryLevel =              GetBatteryLevel,
  .GetTemperature =               GetTemperatureLevel,
  .GetUniqueId =                  GetUniqueId,
  .GetDevAddr =                   GetDevAddr,
  .OnRestoreContextRequest =      OnRestoreContextRequest,
  .OnStoreContextRequest =        OnStoreContextRequest,
  .OnMacProcess =                 OnMacProcessNotify,
  .OnNvmDataChange =              OnNvmDataChange,
  .OnJoinRequest =                OnJoinRequest,
  .OnTxData =                     OnTxData,
  .OnRxData =                     OnRxData,
  .OnBeaconStatusChange =         OnBeaconStatusChange,
  .OnSysTimeUpdate =              OnSysTimeUpdate,
  .OnClassChange =                OnClassChange,
  .OnTxPeriodicityChanged =       OnTxPeriodicityChanged,
  .OnTxFrameCtrlChanged =         OnTxFrameCtrlChanged,
  .OnPingSlotPeriodicityChanged = OnPingSlotPeriodicityChanged,
  .OnSystemReset =                OnSystemReset,
};

/**
  * @brief LoRaWAN handler parameters
  */
static LmHandlerParams_t LmHandlerParams =
{
  .ActiveRegion =             ACTIVE_REGION,
  .DefaultClass =             LORAWAN_DEFAULT_CLASS,
  .AdrEnable =                LORAWAN_ADR_STATE,
  .IsTxConfirmed =            LORAWAN_DEFAULT_CONFIRMED_MSG_STATE,
  .TxDatarate =               LORAWAN_DEFAULT_DATA_RATE,
  .TxPower =                  LORAWAN_DEFAULT_TX_POWER,
  .PingSlotPeriodicity =      LORAWAN_DEFAULT_PING_SLOT_PERIODICITY,
  .RxBCTimeout =              LORAWAN_DEFAULT_CLASS_B_C_RESP_TIMEOUT
};

/**
  * @brief Type of Event to generate application Tx
  */
static TxEventType_t EventType = TX_ON_TIMER;

/**
  * @brief Timer to handle the application Tx
  */
static UTIL_TIMER_Object_t TxTimer;

/**
  * @brief Tx Timer period
  */
static UTIL_TIMER_Time_t TxPeriodicity = APP_TX_DUTYCYCLE;

/**
  * @brief Join Timer period
  */
static UTIL_TIMER_Object_t StopJoinTimer;

/* USER CODE BEGIN PV */
/**
  * @brief User application buffer
  */
static uint8_t AppDataBuffer[LORAWAN_APP_DATA_BUFFER_MAX_SIZE];

/**
  * @brief User application data structure
  */
static LmHandlerAppData_t AppData = { 0, 0, AppDataBuffer };

/**
  * @brief Specifies the state of the application LED
  */
static uint8_t AppLedStateOn = RESET;

/**
  * @brief Timer to handle the application Tx Led to toggle
  */
static UTIL_TIMER_Object_t TxLedTimer;

/**
  * @brief Timer to handle the application Rx Led to toggle
  */
static UTIL_TIMER_Object_t RxLedTimer;

/**
  * @brief Timer to handle the application Join Led to toggle
  */
static UTIL_TIMER_Object_t JoinLedTimer;

/**
  * @brief Timer to handle the YunHorn STS RSS WakeUP Checking
  */
//static UTIL_TIMER_Object_t YunhornSTSRSSWakeUpTimer;

/**
  * @brief Timer to handle the YunHorn STS Sensor Sampling Process
  */
//static UTIL_TIMER_Object_t YunhornSTSSamplingCheckTimer;

/**
  * @brief Timer to handle the YunHorn STS Sensor Uploading Process
  */
//static UTIL_TIMER_Object_t YunhornSTSUploadingMessageTimer;

/**
  * @brief Timer to handle the YunHorn STS Sensor Heart Beat Process
  */
static UTIL_TIMER_Object_t YunhornSTSHeartBeatTimer;

/**
  * @brief Heart-Beat Timer period
  */
//static UTIL_TIMER_Time_t HeartBeatPeriodicity = 30*APP_TX_DUTYCYCLE;		// 5 MIN initial

/* USER CODE END PV */

/* Exported functions ---------------------------------------------------------*/
/* USER CODE BEGIN EF */

/* USER CODE END EF */

void LoRaWAN_Init(void)
{
  /* USER CODE BEGIN LoRaWAN_Init_LV */
  uint32_t feature_version = 0UL;
  /* USER CODE END LoRaWAN_Init_LV */

  /* USER CODE BEGIN LoRaWAN_Init_1 */
  APP_LOG(TS_OFF, VLEVEL_L, "\r\n\n\n##### YUNHORN_STS_FW:%d SWV%d HWV:%d MTM:%d.%d R:%d.%d.%d ####\r\n\n\n",
  	FirmwareVersion, sts_version, sts_hardware_ver, sts_mtmcode1,sts_mtmcode2, MajorVer, MinorVer, SubMinorVer);

  /* Get LoRaWAN APP version*/
  APP_LOG(TS_OFF, VLEVEL_M, "APPLICATION_VERSION: V%X.%X.%X\r\n",
          (uint8_t)(APP_VERSION_MAIN),
          (uint8_t)(APP_VERSION_SUB1),
          (uint8_t)(APP_VERSION_SUB2));

  /* Get MW LoRaWAN info */
  APP_LOG(TS_OFF, VLEVEL_M, "MW_LORAWAN_VERSION:  V%X.%X.%X\r\n",
          (uint8_t)(LORAWAN_VERSION_MAIN),
          (uint8_t)(LORAWAN_VERSION_SUB1),
          (uint8_t)(LORAWAN_VERSION_SUB2));

  /* Get MW SubGhz_Phy info */
  APP_LOG(TS_OFF, VLEVEL_M, "MW_RADIO_VERSION:    V%X.%X.%X\r\n",
          (uint8_t)(SUBGHZ_PHY_VERSION_MAIN),
          (uint8_t)(SUBGHZ_PHY_VERSION_SUB1),
          (uint8_t)(SUBGHZ_PHY_VERSION_SUB2));

  /* Get LoRaWAN Link Layer info */
  LmHandlerGetVersion(LORAMAC_HANDLER_L2_VERSION, &feature_version);
  APP_LOG(TS_OFF, VLEVEL_M, "L2_SPEC_VERSION:     V%X.%X.%X\r\n",
          (uint8_t)(feature_version >> 24),
          (uint8_t)(feature_version >> 16),
          (uint8_t)(feature_version >> 8));

  /* Get LoRaWAN Regional Parameters info */
  LmHandlerGetVersion(LORAMAC_HANDLER_REGION_VERSION, &feature_version);
  APP_LOG(TS_OFF, VLEVEL_M, "RP_SPEC_VERSION:     V%X-%X.%X.%X\r\n",
          (uint8_t)(feature_version >> 24),
          (uint8_t)(feature_version >> 16),
          (uint8_t)(feature_version >> 8),
          (uint8_t)(feature_version));

  UTIL_TIMER_Create(&TxLedTimer, LED_PERIOD_TIME, UTIL_TIMER_ONESHOT, OnTxTimerLedEvent, NULL);
  UTIL_TIMER_Create(&RxLedTimer, LED_PERIOD_TIME, UTIL_TIMER_ONESHOT, OnRxTimerLedEvent, NULL);
  UTIL_TIMER_Create(&JoinLedTimer, LED_PERIOD_TIME, UTIL_TIMER_PERIODIC, OnJoinTimerLedEvent, NULL);

  if (FLASH_IF_Init(NULL) != FLASH_IF_OK)
  {
    Error_Handler();
  }

  /* USER CODE END LoRaWAN_Init_1 */

  UTIL_TIMER_Create(&StopJoinTimer, JOIN_TIME, UTIL_TIMER_ONESHOT, OnStopJoinTimerEvent, NULL);

  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LmHandlerProcess), UTIL_SEQ_RFU, LmHandlerProcess);

  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), UTIL_SEQ_RFU, SendTxData);
  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LoRaStoreContextEvent), UTIL_SEQ_RFU, StoreContext);
  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_LoRaStopJoinEvent), UTIL_SEQ_RFU, StopJoin);

  /* Init Info table used by LmHandler*/
  LoraInfo_Init();

  /* Init the Lora Stack*/
  LmHandlerInit(&LmHandlerCallbacks, APP_VERSION);

  LmHandlerConfigure(&LmHandlerParams);

  /* USER CODE BEGIN LoRaWAN_Init_2 */
  UTIL_TIMER_Start(&JoinLedTimer);

  /* USER CODE END LoRaWAN_Init_2 */

  LmHandlerJoin(ActivationType, ForceRejoin);

  if (EventType == TX_ON_TIMER)
  {
    /* send every time timer elapses */
    UTIL_TIMER_Create(&TxTimer, TxPeriodicity, UTIL_TIMER_ONESHOT, OnTxTimerEvent, NULL);
    UTIL_TIMER_Start(&TxTimer);
  }
  else
  {
    /* USER CODE BEGIN LoRaWAN_Init_3 */

    /* USER CODE END LoRaWAN_Init_3 */
  }

  /* USER CODE BEGIN LoRaWAN_Init_Last */

  STS_REBOOT_CONFIG_Init();


//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), UTIL_SEQ_RFU, STS_YunhornSTSEventP1_Process);
  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventRFAC), UTIL_SEQ_RFU, STS_YunhornSTSEventRFAC_Process);

//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP2), UTIL_SEQ_RFU, STS_YunhornSTSEventP2_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP3), UTIL_SEQ_RFU, STS_YunhornSTSEventP3_Process);
  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP4), UTIL_SEQ_RFU, STS_YunhornSTSEventP4_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP5), UTIL_SEQ_RFU, STS_YunhornSTSEventP5_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP6), UTIL_SEQ_RFU, STS_YunhornSTSEventP6_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP7), UTIL_SEQ_RFU, STS_YunhornSTSEventP7_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP8), UTIL_SEQ_RFU, STS_YunhornSTSEventP8_Process);
//  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventPIORS485), UTIL_SEQ_RFU, STS_YunhornSTSEventPIORS485_Process);

#ifdef YUNHORN_STS_O6_ENABLED
  UTIL_TIMER_Create(&YunhornSTSRSSWakeUpTimer,
		  YUNHORN_STS_RSS_WAKEUP_CHECK_TIME,
		  UTIL_TIMER_PERIODIC, OnYunhornSTSOORSSWakeUpTimerEvent, NULL);
  UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);
#endif

  // Uploading Message
  // FOR Periodically upload message sensor, use this as Heart Beat interval
#ifdef	YUNHORN_STS_MM_ENABLED
  UTIL_TIMER_Create(&YunhornSTSUploadingMessageTimer,
		  YUNHORN_STS_UPLOADING_MESSAGE_TIME,
		  UTIL_TIMER_PERIODIC, OnYunhornSTSUploadingMessageEvent, NULL);
  UTIL_TIMER_Start(&YunhornSTSUploadingMessageTimer);
#endif

  // for IAQ sensor, multiple sampling, result in one average result between upload interval
#if	(defined(YUNHORN_STS_E0_ENABLED))
  // Samping sensor interval
  UTIL_TIMER_Create(&YunhornSTSSamplingCheckTimer,
		  YUNHORN_STS_SAMPLING_CHECK_TIME,
  		  UTIL_TIMER_PERIODIC, OnYunhornSTSSamplingCheckTimerEvent, NULL);
    UTIL_TIMER_Start(&YunhornSTSSamplingCheckTimer);
#endif

    // Heart Beat Timer
    // FOR Event trigger type sensor, use this as Heart Beat Timer, such as O1/O2/O3/M1
#if	(defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED))
    UTIL_TIMER_Create(&YunhornSTSHeartBeatTimer, HeartBeatPeriodicity,
    		UTIL_TIMER_ONESHOT, OnYunhornSTSHeartBeatTimerEvent, NULL);
        UTIL_TIMER_Start(&YunhornSTSHeartBeatTimer);
#endif



  /* USER CODE END LoRaWAN_Init_Last */
}

/* USER CODE BEGIN PB_Callbacks */

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  switch (GPIO_Pin)
  {
    case  BUT1_Pin:
      if (EventType == TX_ON_EVENT)
      {
#ifdef	YUNHORN_STS_O5_ENABLED
          UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), CFG_SEQ_Prio_0);
#endif

      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
#ifdef LED_ONBOARD
    		LED_ON;
    		HAL_Delay(20);
    		LED_OFF;
#endif
      }
      break;
    case  BUT2_Pin:
#if defined(STS_USE_STM32WLE5)
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStopJoinEvent), CFG_SEQ_Prio_0);
#endif
      break;
#if defined(STS_USE_STM32WL55)
    case  BUT3_Pin:

      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStoreContextEvent), CFG_SEQ_Prio_0);
#endif
      break;
#if (defined(STS_USE_TOF_VL53L1X) ||defined(STS_USE_TOF_VL53L0X))
    case TOF_INT_EXTI_PIN:
    	 ToF_EventDetected = 1;
    //	 APP_LOG(TS_OFF,VLEVEL_L, "##################################### TOF_EVENT DETECTED \r\n");
    	 break;
#endif
    default:
      break;
  }
}


/* USER CODE END PB_Callbacks */

/* Private functions ---------------------------------------------------------*/
/* USER CODE BEGIN PrFD */

/* USER CODE END PrFD */

static void OnRxData(LmHandlerAppData_t *appData, LmHandlerRxParams_t *params)
{
  /* USER CODE BEGIN OnRxData_1 */
  uint8_t RxPort = 0;

  if (params != NULL)
  {
#if	defined(STM32WL55xx)
    HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */
#endif

    UTIL_TIMER_Start(&RxLedTimer);

    if (params->IsMcpsIndication)
    {
      if (appData != NULL)
      {
        RxPort = appData->Port;
        if (appData->Buffer != NULL)
        {
          switch (appData->Port)
          {
            case LORAWAN_SWITCH_CLASS_PORT:
              /*this port switches the class*/
              if (appData->BufferSize == 1)
              {
                switch (appData->Buffer[0])
                {
                  case 0:
                  {
                    LmHandlerRequestClass(CLASS_A);
                    break;
                  }
                  case 1:
                  {
                    LmHandlerRequestClass(CLASS_B);
                    break;
                  }
                  case 2:
                  {
                    LmHandlerRequestClass(CLASS_C);
                    break;
                  }
                  default:
                    break;
                }
              }
              DeviceClass_t deviceClass = CLASS_A;
              LmHandlerGetCurrentClass( &deviceClass );
              uint8_t	i=0;
              outbuf[i++] = (uint8_t) 'L';
              outbuf[i++] = (uint8_t) sts_mtmcode1;
              outbuf[i++] = (uint8_t) sts_mtmcode2;
              outbuf[i++] = (uint8_t) sts_version;
              outbuf[i++] = (uint8_t) (0x41+ deviceClass); //translate to 'A','B','C'
              STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

              break;
            case LORAWAN_USER_APP_PORT:
              if (appData->BufferSize == 1)
              {
                AppLedStateOn = appData->Buffer[0] & 0x01;
                if (AppLedStateOn == RESET)
                {
                  APP_LOG(TS_OFF, VLEVEL_H, "LED OFF\r\n");
#if	defined(STM32WL55xx)
                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
#endif
                }
                else
                {
                  APP_LOG(TS_OFF, VLEVEL_H, "LED ON\r\n");
#if	defined(STM32WL55xx)
                  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
#endif
                }
              }
              break;
			case LORAWAN_USER_APP_CTRL_PORT:
			  if (appData->BufferSize != 1)
			  {
				if (appData->BufferSize < 128) {
					USER_APP_AUTO_RESPONDER_Parse((char*)appData->Buffer, appData->BufferSize);
				}
			  }
			break;
            default:

              break;
          }
        }
      }
    }
    if (params->RxSlot < RX_SLOT_NONE)
    {
      APP_LOG(TS_OFF, VLEVEL_H, "###### D/L FRAME:%04d | PORT:%d | DR:%d | SLOT:%s | RSSI:%d | SNR:%d\r\n",
              params->DownlinkCounter, RxPort, params->Datarate, slotStrings[params->RxSlot],
              params->Rssi, params->Snr);
    }
  }
  /* USER CODE END OnRxData_1 */
}

static void SendTxData(void)
{
  /* USER CODE BEGIN SendTxData_1 */
  LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;
  uint8_t batteryLevel = 250;	//GetBatteryLevel();
  //uint16_t batteryLevelmV = SYS_GetBatteryLevel();
  uint16_t batteryLevelmV = 3000;	//STS_GetBatteryLevel();
  //sensor_t sensor_data;
#if	defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)
  STS_R0_SensorDataTypeDef r0_data;
#endif
#if defined(STS_O5)
  STS_OO_SensorDataTypeDef oo_data;
#endif
  UTIL_TIMER_Time_t nextTxIn = 0;

  if (LmHandlerIsBusy() == false)
  {
	  uint8_t i = 0;
#if defined(YUNHORN_STS_O5_ENABLED)
	  STS_O5_SENSOR_Read(&oo_data);
#endif
#if defined(SOAP_LEVEL_SENSOR)
	  STS_YunhornSTSEventP6_Process();
#endif

#if	defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)
	  PME_ON;

	  HAL_Delay(5000);

	  MX_TOF_Init();
	  HAL_Delay(500);

	  LED_ON;
	  MX_TOF_Process();
	  STS_R0_SENSOR_Read(&r0_data);
	  //HAL_Delay(1000);
	  PME_OFF;
	  LED_OFF;
	  r0_data.battery_Pct = (uint8_t)(99*batteryLevel/254);

#endif
	  //	EnvSensors_Read(&sensor_data);
	  //    APP_LOG(TS_ON, VLEVEL_M, "\r\nVDDA: %d\r\n", batteryLevel);
	  //    APP_LOG(TS_ON, VLEVEL_M, "\r\ntemp: %d\r\n", (int16_t)(sensor_data.temperature));

    AppData.Port = LORAWAN_USER_APP_PORT;
#ifdef ROCTEC_R5

#else
    AppData.Buffer[i++] = AppLedStateOn;
    AppData.Buffer[i++] = (uint8_t)(0xFF & sts_mtmcode1); //mtmcode1;		//#02
    AppData.Buffer[i++] = (uint8_t)(0xFF & sts_mtmcode2); //mtmcode2;		//#03
    AppData.Buffer[i++] = (uint8_t)(0xFF & sts_hardware_ver); //hardware_Ver; //#04
    //AppData.Buffer[i++] = (uint8_t)(0xFF & (99*batteryLevel/254));					//#05
    AppData.Buffer[i++] = (uint8_t)(0xFF & (uint8_t)(batteryLevelmV/100));					//#05
#endif

    if (heart_beat_timer != 0U)
    {
		heart_beat_timer = 0U;
		AppData.Buffer[0] = AppLedStateOn|0X80;
		AppData.Port = LORAWAN_USER_HTBT_PORT; //LORAWAN_USER_APP_PORT+1;
#ifdef ROCTEC_R5
	    AppData.Buffer[i++] = AppLedStateOn;
	    AppData.Buffer[i++] = (uint8_t)(r0_data.battery_Pct);
#endif

    } else if ((upload_message_timer !=0U)||(sensor_data_ready!=0U)) //sensor_data_ready for manual push button-1 trigger)
    {
    	sensor_data_ready =0;
    	upload_message_timer =0;
#ifdef ROCTEC_R5
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance1_mm >>8)&0xff;					//#05
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance1_mm)&0xff;						//#06
#endif
#ifdef STS_O5
    	AppData.Buffer[i++] = (uint8_t)(oo_data.state_sensor1_on_off)&0xff;
#endif
#ifdef	STS_R1D													// TOF_1 & TOF_2 for STS_R1D
    	AppData.Buffer[i++] = (uint8_t)(0x04)&0xff;										//#length of following bytes
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance_mm >>8)&0xff;					//#05
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance_mm)&0xff;						//#06
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance1_mm >>8)&0xff;					//#07
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance1_mm)&0xff;						//#08
#endif
#ifdef TOF_3
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance2_mm >>8)&0xff;					//#09
    	AppData.Buffer[i++] = (uint8_t)(r0_data.distance2_mm)&0xff;						//#10
#endif
#ifdef SOAP_LEVEL
    	AppData.Buffer[i++] = (uint8_t)(sts_soap_level_state)&0xff;							//#11
#endif
    	//AppData.Buffer[i++] = (uint8_t)(r0_data.battery_mV >>8)&0xff;						//#12
    	//AppData.Buffer[i++] = (uint8_t)(r0_data.battery_mV/100)&0xff;							//#13
    	AppData.Buffer[i++] = (uint8_t)(batteryLevelmV/100)&0xff;							//#13


    }

    AppData.BufferSize = (sts_service_mask >1?0:i);

    if ((JoinLedTimer.IsRunning) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET))
    {
      UTIL_TIMER_Stop(&JoinLedTimer);
#if	defined(STM32WL55xx)
      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
#endif
    }

    status = LmHandlerSend(&AppData, LmHandlerParams.IsTxConfirmed, false);
    if (LORAMAC_HANDLER_SUCCESS == status)
    {
      APP_LOG(TS_ON, VLEVEL_H, "SEND REQUEST\r\n");
    }
    else if (LORAMAC_HANDLER_DUTYCYCLE_RESTRICTED == status)
    {
      nextTxIn = LmHandlerGetDutyCycleWaitTime();
      if (nextTxIn > 0)
      {
        APP_LOG(TS_ON, VLEVEL_L, "Next Tx in  : ~%d second(s)\r\n", (nextTxIn / 1000));
      }
    }
  }

  if (EventType == TX_ON_TIMER)
  {
    UTIL_TIMER_Stop(&TxTimer);
    UTIL_TIMER_SetPeriod(&TxTimer, MAX(nextTxIn, TxPeriodicity));
    UTIL_TIMER_Start(&TxTimer);
  }
  LED_OFF;
  /* USER CODE END SendTxData_1 */
}

static void OnTxTimerEvent(void *context)
{
  /* USER CODE BEGIN OnTxTimerEvent_1 */

  /* USER CODE END OnTxTimerEvent_1 */
  //upload_message_timer =1U;

  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);

  /*Wait for next tx slot*/
  UTIL_TIMER_Start(&TxTimer);
  /* USER CODE BEGIN OnTxTimerEvent_2 */

  /* USER CODE END OnTxTimerEvent_2 */
}

/* USER CODE BEGIN PrFD_LedEvents */
static void OnTxTimerLedEvent(void *context)
{
#if	defined(STM32WL55xx)
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); /* LED_GREEN */
#endif
}

static void OnRxTimerLedEvent(void *context)
{
#if	defined(STM32WL55xx)
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* LED_BLUE */
#endif
}

static void OnJoinTimerLedEvent(void *context)
{
#if	defined(STM32WL55xx)
  HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); /* LED_RED */
#endif
}

/* USER CODE END PrFD_LedEvents */

static void OnTxData(LmHandlerTxParams_t *params)
{
  /* USER CODE BEGIN OnTxData_1 */
  if ((params != NULL))
  {
    /* Process Tx event only if its a mcps response to prevent some internal events (mlme) */
    if (params->IsMcpsConfirm != 0)
    {
#if	defined(STM32WL55xx)
      HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); /* LED_GREEN */
#endif
      UTIL_TIMER_Start(&TxLedTimer);

      APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### ========== MCPS-Confirm =============\r\n");
      APP_LOG(TS_OFF, VLEVEL_H, "###### U/L FRAME:%04d | PORT:%d | DR:%d | PWR:%d", params->UplinkCounter,
              params->AppData.Port, params->Datarate, params->TxPower);

      APP_LOG(TS_OFF, VLEVEL_H, " | MSG TYPE:");
      if (params->MsgType == LORAMAC_HANDLER_CONFIRMED_MSG)
      {
        APP_LOG(TS_OFF, VLEVEL_H, "CONFIRMED [%s]\r\n", (params->AckReceived != 0) ? "ACK" : "NACK");
      }
      else
      {
        APP_LOG(TS_OFF, VLEVEL_H, "UNCONFIRMED\r\n");
      }
    }
  }
  /* USER CODE END OnTxData_1 */
}

static void OnJoinRequest(LmHandlerJoinParams_t *joinParams)
{
  /* USER CODE BEGIN OnJoinRequest_1 */
  if (joinParams != NULL)
  {
    if (joinParams->Status == LORAMAC_HANDLER_SUCCESS)
    {
      UTIL_TIMER_Stop(&JoinLedTimer);
#if	defined(STM32WL55xx)
      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
#endif

      APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### = JOINED = ");
      if (joinParams->Mode == ACTIVATION_TYPE_ABP)
      {
        APP_LOG(TS_OFF, VLEVEL_M, "ABP ======================\r\n");
      }
      else
      {
        APP_LOG(TS_OFF, VLEVEL_M, "OTAA =====================\r\n");
      }

      STS_LoRa_WAN_Joined = (uint8_t) joinParams->Mode;
      AppData.Port = 1;
      AppData.BufferSize = 10;
      UTIL_MEM_cpy_8((uint8_t*)AppData.Buffer, "YUNHORN168", 10);
      LmHandlerParams.IsTxConfirmed = true;
      LmHandlerErrorStatus_t status = LmHandlerSend(&AppData, LmHandlerParams.IsTxConfirmed, false);
      if (status ==LORAMAC_HANDLER_SUCCESS )  LmHandlerParams.IsTxConfirmed = false;

      OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);
      OnTxPeriodicityChanged(TxPeriodicity);
      //APP_LOG(TS_OFF, VLEVEL_L,"\r\n STS_LoRa_WAN_Joined = %s \r\n", (STS_LoRa_WAN_Joined == 1)?"ABP":"OTAA");
    }
    else
    {
      APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### = JOIN FAILED\r\n");
    }

    APP_LOG(TS_OFF, VLEVEL_M, "###### U/L FRAME:JOIN | DR:%d | PWR:%d\r\n", joinParams->Datarate, joinParams->TxPower);
  }
  //heart_beat_timer = 1;
  //SendTxData();
  //UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
  //STS_SENSOR_Upload_Message(LORAWAN_USER_HTBT_PORT, 12,"Yunhorn88STS");
  //STS_SENSOR_Upload_Message(LORAWAN_USER_HTBT_PORT, 12,"Yunhorn88STS");

  /* USER CODE END OnJoinRequest_1 */
}

static void OnBeaconStatusChange(LmHandlerBeaconParams_t *params)
{
  /* USER CODE BEGIN OnBeaconStatusChange_1 */
  if (params != NULL)
  {
    switch (params->State)
    {
      default:
      case LORAMAC_HANDLER_BEACON_LOST:
      {
        APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### BEACON LOST\r\n");
        break;
      }
      case LORAMAC_HANDLER_BEACON_RX:
      {
        APP_LOG(TS_OFF, VLEVEL_M,
                "\r\n###### BEACON RECEIVED | DR:%d | RSSI:%d | SNR:%d | FQ:%d | TIME:%d | DESC:%d | "
                "INFO:02X%02X%02X %02X%02X%02X\r\n",
                params->Info.Datarate, params->Info.Rssi, params->Info.Snr, params->Info.Frequency,
                params->Info.Time.Seconds, params->Info.GwSpecific.InfoDesc,
                params->Info.GwSpecific.Info[0], params->Info.GwSpecific.Info[1],
                params->Info.GwSpecific.Info[2], params->Info.GwSpecific.Info[3],
                params->Info.GwSpecific.Info[4], params->Info.GwSpecific.Info[5]);
        break;
      }
      case LORAMAC_HANDLER_BEACON_NRX:
      {
        APP_LOG(TS_OFF, VLEVEL_M, "\r\n###### BEACON NOT RECEIVED\r\n");
        break;
      }
    }
  }
  /* USER CODE END OnBeaconStatusChange_1 */
}

static void OnSysTimeUpdate(void)
{
  /* USER CODE BEGIN OnSysTimeUpdate_1 */

  /* USER CODE END OnSysTimeUpdate_1 */
}

static void OnClassChange(DeviceClass_t deviceClass)
{
  /* USER CODE BEGIN OnClassChange_1 */
  APP_LOG(TS_OFF, VLEVEL_M, "Switch to Class %c done\r\n", "ABC"[deviceClass]);
  /* USER CODE END OnClassChange_1 */
}

static void OnMacProcessNotify(void)
{
  /* USER CODE BEGIN OnMacProcessNotify_1 */

  /* USER CODE END OnMacProcessNotify_1 */
  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LmHandlerProcess), CFG_SEQ_Prio_0);

  /* USER CODE BEGIN OnMacProcessNotify_2 */

  /* USER CODE END OnMacProcessNotify_2 */
}

static void OnTxPeriodicityChanged(uint32_t periodicity)
{
  /* USER CODE BEGIN OnTxPeriodicityChanged_1 */

  /* USER CODE END OnTxPeriodicityChanged_1 */
  TxPeriodicity = periodicity;

  if (TxPeriodicity == 0)
  {
    /* Revert to application default periodicity */
    TxPeriodicity = APP_TX_DUTYCYCLE;
  }

  /* Update timer periodicity */
  UTIL_TIMER_Stop(&TxTimer);
  UTIL_TIMER_SetPeriod(&TxTimer, TxPeriodicity);
  UTIL_TIMER_Start(&TxTimer);
  /* USER CODE BEGIN OnTxPeriodicityChanged_2 */
  //APP_LOG(TS_OFF, VLEVEL_H,"\r\n TxPeriodicity changed to %u (msec) \r\n", TxPeriodicity);
  /* USER CODE END OnTxPeriodicityChanged_2 */
}

static void OnTxFrameCtrlChanged(LmHandlerMsgTypes_t isTxConfirmed)
{
  /* USER CODE BEGIN OnTxFrameCtrlChanged_1 */

  /* USER CODE END OnTxFrameCtrlChanged_1 */
  LmHandlerParams.IsTxConfirmed = isTxConfirmed;
  /* USER CODE BEGIN OnTxFrameCtrlChanged_2 */

  /* USER CODE END OnTxFrameCtrlChanged_2 */
}

static void OnPingSlotPeriodicityChanged(uint8_t pingSlotPeriodicity)
{
  /* USER CODE BEGIN OnPingSlotPeriodicityChanged_1 */

  /* USER CODE END OnPingSlotPeriodicityChanged_1 */
  LmHandlerParams.PingSlotPeriodicity = pingSlotPeriodicity;
  /* USER CODE BEGIN OnPingSlotPeriodicityChanged_2 */

  /* USER CODE END OnPingSlotPeriodicityChanged_2 */
}

static void OnSystemReset(void)
{
  /* USER CODE BEGIN OnSystemReset_1 */

  /* USER CODE END OnSystemReset_1 */
  if ((LORAMAC_HANDLER_SUCCESS == LmHandlerHalt()) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET))
  {
    NVIC_SystemReset();
  }
  /* USER CODE BEGIN OnSystemReset_Last */

  /* USER CODE END OnSystemReset_Last */
}

static void StopJoin(void)
{
  /* USER CODE BEGIN StopJoin_1 */
#if	defined(STM32WL55xx)
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); /* LED_BLUE */
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET); /* LED_GREEN */
  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET); /* LED_RED */
#endif
  /* USER CODE END StopJoin_1 */

  UTIL_TIMER_Stop(&TxTimer);

  if (LORAMAC_HANDLER_SUCCESS != LmHandlerStop())
  {
    APP_LOG(TS_OFF, VLEVEL_M, "LmHandler Stop on going ...\r\n");
  }
  else
  {
    APP_LOG(TS_OFF, VLEVEL_M, "LmHandler Stopped\r\n");
    if (LORAWAN_DEFAULT_ACTIVATION_TYPE == ACTIVATION_TYPE_ABP)
    {
      ActivationType = ACTIVATION_TYPE_OTAA;
      APP_LOG(TS_OFF, VLEVEL_M, "LmHandler switch to OTAA mode\r\n");
    }
    else
    {
      ActivationType = ACTIVATION_TYPE_ABP;
      APP_LOG(TS_OFF, VLEVEL_M, "LmHandler switch to ABP mode\r\n");
    }
    LmHandlerConfigure(&LmHandlerParams);
    LmHandlerJoin(ActivationType, true);
    UTIL_TIMER_Start(&TxTimer);
  }
  UTIL_TIMER_Start(&StopJoinTimer);
  /* USER CODE BEGIN StopJoin_Last */

  /* USER CODE END StopJoin_Last */
}

static void OnStopJoinTimerEvent(void *context)
{
  /* USER CODE BEGIN OnStopJoinTimerEvent_1 */

  /* USER CODE END OnStopJoinTimerEvent_1 */
  if (ActivationType == LORAWAN_DEFAULT_ACTIVATION_TYPE)
  {
    UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStopJoinEvent), CFG_SEQ_Prio_0);
  }
  /* USER CODE BEGIN OnStopJoinTimerEvent_Last */
#if	defined(STM32WL55xx)
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* LED_BLUE */
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); /* LED_GREEN */
  HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
#endif
  /* USER CODE END OnStopJoinTimerEvent_Last */
}

static void StoreContext(void)
{
  LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;

  /* USER CODE BEGIN StoreContext_1 */

  /* USER CODE END StoreContext_1 */
  status = LmHandlerNvmDataStore();

  if (status == LORAMAC_HANDLER_NVM_DATA_UP_TO_DATE)
  {
    APP_LOG(TS_OFF, VLEVEL_M, "NVM DATA UP TO DATE\r\n");
  }
  else if (status == LORAMAC_HANDLER_ERROR)
  {
    APP_LOG(TS_OFF, VLEVEL_M, "NVM DATA STORE FAILED\r\n");
  }
  /* USER CODE BEGIN StoreContext_Last */

  /* USER CODE END StoreContext_Last */
}

static void OnNvmDataChange(LmHandlerNvmContextStates_t state)
{
  /* USER CODE BEGIN OnNvmDataChange_1 */

  /* USER CODE END OnNvmDataChange_1 */
  if (state == LORAMAC_HANDLER_NVM_STORE)
  {
    APP_LOG(TS_OFF, VLEVEL_M, "NVM DATA STORED\r\n");
  }
  else
  {
    APP_LOG(TS_OFF, VLEVEL_M, "NVM DATA RESTORED\r\n");
  }
  /* USER CODE BEGIN OnNvmDataChange_Last */

  /* USER CODE END OnNvmDataChange_Last */
}

static void OnStoreContextRequest(void *nvm, uint32_t nvm_size)
{
  /* USER CODE BEGIN OnStoreContextRequest_1 */

  /* USER CODE END OnStoreContextRequest_1 */
  /* store nvm in flash */
  if (FLASH_IF_Erase(LORAWAN_NVM_BASE_ADDRESS, FLASH_PAGE_SIZE) == FLASH_IF_OK)
  {
    FLASH_IF_Write(LORAWAN_NVM_BASE_ADDRESS, (const void *)nvm, nvm_size);
  }
  /* USER CODE BEGIN OnStoreContextRequest_Last */

  /* USER CODE END OnStoreContextRequest_Last */
}

static void OnRestoreContextRequest(void *nvm, uint32_t nvm_size)
{
  /* USER CODE BEGIN OnRestoreContextRequest_1 */

  /* USER CODE END OnRestoreContextRequest_1 */
  FLASH_IF_Read(nvm, LORAWAN_NVM_BASE_ADDRESS, nvm_size);
  /* USER CODE BEGIN OnRestoreContextRequest_Last */

  /* USER CODE END OnRestoreContextRequest_Last */
}


/**
  * @brief  Yunhorn STS Occupancy RSS WakeUP timer callback function, act as sampling process
  * @param  context ptr of STS RSS WakeUp context
  */
/*
static void OnYunhornSTSOORSSWakeUpTimerEvent(void *context)
{
	UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP2), CFG_SEQ_Prio_0);

	if (STS_LoRa_WAN_Joined != 0)
	{
	    	UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);

	}
}
*/
/**
  * @brief  Yunhorn STS  Sensor Live Heart Beat Periodicity/interval Change callback function
  * @param  context ptr of STS Live Heart Beat context
  * YL x x x
  * */

static void OnYunhornSTSHeartBeatPeriodicityChanged(uint32_t periodicity)
{
  /* USER CODE BEGIN OnTxPeriodicityChanged_1 */

  /* USER CODE END OnTxPeriodicityChanged_1 */
	HeartBeatPeriodicity = periodicity;

  if (HeartBeatPeriodicity == 0)
  {
    /* Revert to application default periodicity */
	  HeartBeatPeriodicity = 10*APP_TX_DUTYCYCLE;
  }

  /* Update timer periodicity */

	  UTIL_TIMER_Stop(&YunhornSTSHeartBeatTimer);
	  UTIL_TIMER_SetPeriod(&YunhornSTSHeartBeatTimer, HeartBeatPeriodicity);
	  UTIL_TIMER_Start(&YunhornSTSHeartBeatTimer);

  /* USER CODE BEGIN OnTxPeriodicityChanged_2 */
  //APP_LOG(TS_OFF, VLEVEL_H,"**************** HeartBeatPeriodicity Changed to: %u (ms)\r\n", HeartBeatPeriodicity );
  /* USER CODE END OnTxPeriodicityChanged_2 */
}




/*
  	    // UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP4), CFG_SEQ_Prio_0);
	  	// P1    --- REEDSWITCH, HALL ELEMENT, WATER LEAKAGE
	   	// P2    --- SEE ABOVE, RSS PRESENCE
	   	// P3    ---- LAMP BAR SCOLLER PROCESS
	   	// P4    --- TOF DISTANCE  VL53L0X simple distance
	   	// P5    --- TOF IN OUT COUNT  VL53L3X in out or duration
	   	// P6    ---
	   	// P7    ---
	   	// P8    --- AIR QUALITY AND ODOR LEVEL, SMOKING DETECTION
*/

/**
  * @brief  Yunhorn STS Sensor Sampling Check Timer callback function
  * @param  context ptr of STS Sampling Check context
  */
/*
static void OnYunhornSTSSamplingCheckTimerEvent(void *context)
{
	    if (STS_LoRa_WAN_Joined )
	    {
#ifdef YUNHORN_STS_R0_ENABLED

	    	upload_message_timer = 1U;
	    	UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP4), CFG_SEQ_Prio_0);
	    	UTIL_TIMER_Start(&YunhornSTSSamplingCheckTimer);
#endif
	    }
}
*/

/**
  * @brief  Yunhorn STS Sensor Heart Beat Timer callback function
  * @param  context ptr of STS Sampling Check context
  */
static void OnYunhornSTSHeartBeatTimerEvent(void *context)
{
	heart_beat_timer = 1;
	UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
	UTIL_TIMER_Start(&YunhornSTSHeartBeatTimer);

	if ((STS_LoRa_WAN_Joined ) && (sts_ac_code[0]==0x0) && (sts_ac_code[19]==0x0))
	{
	  		/* RFAC  Challenge */
	  		if (rfac_timer < (STS_BURN_IN_RFAC+3)) {
	  			rfac_timer ++;
	  		}
	   		UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventRFAC), CFG_SEQ_Prio_0);
	}
}

void USER_APP_AUTO_RESPONDER_Parse(char *tlv_buf, size_t tlv_buf_size)
{
	uint8_t i=0, mems_ver, invalid_flag=1;


	/*
	 * YUNHORN STS PRODUCT BOARD LEVEL CONTROL OR REPORT
	 *          --Y
	 *              --Z 			BOARD LEVEL
	 *                 --H 'YZH'			Hardware REBOOT  				[YZH] WVpI
	 *                 --C 'YZC'            LoRa-WAN CLASS A/B/C  			[YZC] WVpD
	 *                 --S 'YZS'            SELF-TEST FUNCTION TEST			[YZS] WVpT
	 *                 --D 'YZD'			DISTANCE MEASURE				[YZD] WVpE
	 *
	 *
	 *              --V				VERSION
	 *                 --H 'YVH'			HARDWARE/FIRMWARE VERSION		[YVH] WVZI
	 *                 --C 'YVC'			NVM CONFIG VERSION				[YVC] WVZD
	 *
	 *              --O				POWER-ON
	 *                 --# 'YO1', 'YO2','YO3'								[YO0] WU8w, [YO1] WU8x, [YO2] WU8y
	 *
	 *              --F				POWER-OFF
	 *                 --# 'YF1', 'YF2','YF3'								[YF0] WUYw, [YF1] WUYx, [YF2] WUYy
	 *
	 *              --H				MEMS RESET
	 *                 --# 'YH0','YH1','YH2'								[YH0] WUgw, [YH1] WUgx, [YH2] WUgy
	 *
	 *              --M				SERVICE LEVEL MASK
	 *                 --# 'YM0', 'YM1','YM2', 'YM3'						[YM0] WU0w [YM1]WU0x [YM2]WU0y [YM3]WU0z
	 *
	 *              --D				UPLINK DURATION OR PERIODICITY			Periodicity of Tx interval or Heart-Beat interval for real-time occupancy status update  2023-04-28
	 *                 --#
	 *                    --#
	 *                       --U		UNIT (S, M, H) SECONDS, MINUTES, HOURS
	 *              --S				SAMPLING INTERVAL OR PERIODICITY        periodicity for real-time sensing
	 *                 --#
	 *                    --#
	 *                       --U		UNIT (S, M, H) SECONDS, MINUTES, HOURS

	 *
	 *          --P					***** WORKMODE AND NETWORK COLOR
	 *               --#			MTM-VER   1
	 *                  --#         STS-VER   1
	 *                     --M   	WORK MODE
	 *
	 *                     --N		NETWORK WORK MODE  {0,1,2,3,4,5,6}
	 *                        --C	NETWORK COLOR, {0,1,2,3,4,5,6,7,8,9}
	 *
	 *          --P					***** SIMPLE CONFIG PARAMETER FOR RSS  (8 DIGITS)
	 *               --#							MTM-VER   1
	 *                  --#         				STS-VER   1
	 *
	 *                     --##						START   #.# meter
	 *                         --##					LENGTH  #.# meter
	 *                             --##				THRESHOLD	#.#*1000
	 *                                 --##   		GAIN		0.##
	 *
	 *          --P					***** DISTANCE MEASURE CONFIG PARAMETER FOR RSS  (7 DIGITS)
	 *               --#							MTM-VER   1
	 *                  --#         				STS-VER   1
	 *                     --##						START   #.# meter
	 *                         --##					LENGTH  #.# meter
	 *                             --#				PROFILE	#
	 *                                 --##   		HWAAS	##
	 *
	 **          --P					***** FULL CONFIG PARAMETER FOR RSS  (30 DIGITS)
	 *               --#							MTM-VER   1
	 *                  --#         				STS-VER   1
	 *						--30{#}		FULL CONFIG PARAMETER
	 *	         --A					***** AC CODE   (22 BYTES)
	 *               --C
	 *                  --#
	 *						--20{#}		AC CODE 20 BYTES
	 *
	 */

	if (((char)tlv_buf[CFG_CMD1] == 'Y') && (tlv_buf_size <=5))  // BEGIN OF *** BOARD LEVEL CONTROL OR REPORT
	{
		switch ((char)tlv_buf[CFG_CMD2])
		{
			case 'Z':		//"YZ"
				if ((char)tlv_buf[CFG_CMD3] == 'H') {  //BOARD SOFT RESET, REVIVE "YZH"
					//BOARD REVIVE
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, 20, (uint8_t *)"!YunHorn STS Revive!");
					HAL_Delay(5000);
					APP_LOG(TS_OFF, VLEVEL_H, "\r\n Yunhorn STS Node Revive ... \r\n");
					HAL_Delay(3000);

					OnSystemReset();

				}	else if 	((char)tlv_buf[CFG_CMD3] == 'S') {		// Self Function Testing "YZS"
					i=0;
					memset((void*)outbuf,0x0,sizeof(outbuf));

					STS_SENSOR_Function_Test_Process();

					HAL_Delay(5000);
					i=21;
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

				} else if 	((char)tlv_buf[CFG_CMD3] == 'C') {		// Lora-WAN Class "YZC" LORAWAN CLASS A/B/C

					DeviceClass_t deviceClass = CLASS_A;
					LmHandlerGetCurrentClass( &deviceClass );
					i=0;
					outbuf[i++] = (uint8_t) 'L';
					outbuf[i++] = (uint8_t) sts_mtmcode1;
					outbuf[i++] = (uint8_t) sts_mtmcode2;
					outbuf[i++] = (uint8_t) sts_version;
					outbuf[i++] = (uint8_t) (0x41+ deviceClass); //translate to 'A','B','C'
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
				} else if 	((char)tlv_buf[CFG_CMD3] == 'D') {		// Distance Measure "YZD"
					i=0;
					outbuf[i++] = (uint8_t) 'Y';
					outbuf[i++] = (uint8_t) 'Z';
					outbuf[i++] = (uint8_t) 'D';
					//STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, outbuf);

					STS_SENSOR_Distance_Test_Process();

					i=0;
					memset((void*)outbuf,0x0,sizeof(outbuf));
					outbuf[i++] 	= (uint8_t)'D';
					outbuf[i++] 	= (uint8_t)sts_mtmcode1;
					outbuf[i++] 	= (uint8_t)sts_mtmcode2;
					outbuf[i++] 	= (uint8_t)sts_version;
					outbuf[i++] 	= (uint8_t)sts_hardware_ver;
					outbuf[i++] 	= (uint8_t)(99*((GetBatteryLevel()/254)&0xff));
#ifdef STS_O6_ENABLED
					outbuf[i++] 	= (uint8_t)0x04; //length of following data
					outbuf[i++] 	= (uint8_t) ((((uint16_t)sts_distance_rss_distance)/1000)%10+0x30)&0xff;
					outbuf[i++] 	= (uint8_t) ((((uint16_t)sts_distance_rss_distance)/100)%10+0x30)&0xff;
					outbuf[i++] 	= (uint8_t) ((((uint16_t)sts_distance_rss_distance)/10)%10+0x30)&0xff;
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_distance_rss_distance)%10+0x30)&0xff;
#endif
#if	(defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)||defined(YUNHORN_STS_R1_ENABLED))
#ifdef	TOF_1
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[0])/100)&0xff;
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[0])%100)&0xff;
#endif
#ifdef	TOF_2
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[1])/100)&0xff;
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[1])%100)&0xff;
#endif

#ifdef	TOF_3
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[2])/100)&0xff;
					outbuf[i++] 	= (uint8_t) (((uint16_t)sts_tof_distance_data[2])%100)&0xff;
#endif
#endif
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

				}

			break;

			case 'M':		//"YM"
				if ((char)tlv_buf[CFG_CMD3] >= '0' && (char)tlv_buf[CFG_CMD3]<='9') 	// Service Mask "YZM"
				{
						sts_service_mask = (uint8_t)(tlv_buf[CFG_CMD3]-0x30)&0xFF;
						sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;
						outbuf[i++] = (uint8_t) 'Y';
						outbuf[i++] = (uint8_t) 'M';
						outbuf[i++] = (uint8_t) (sts_service_mask+0x30);
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
						APP_LOG(TS_OFF, VLEVEL_L, ">>>>>>>>>>>>>>>>>>>>> Mask = [ %02x ] \r\n", sts_service_mask);
						OnStoreSTSCFGContextRequest();
#ifdef YUNHORN_STS_O6_ENABLED
						if (sts_service_mask != STS_SERVICE_MASK_L0) STS_Lamp_Bar_Set_Dark();
						STS_Combined_Status_Processing();
#endif
				}
			break;

			case 'V':	//"YV"
				if ((char)tlv_buf[CFG_CMD3] == 'H') { // "YVH"   REPORT FIRMWARE VERSION "YVH"
					// FIRMWARE VERSION REPORT
					outbuf[i++] = (uint8_t) 'V';
					outbuf[i++] = (uint8_t) sts_mtmcode1;
					outbuf[i++] = (uint8_t) sts_mtmcode2;
					outbuf[i++] = (uint8_t) sts_version;
					outbuf[i++] = (uint8_t) FirmwareVersion;
					outbuf[i++] = (uint8_t) MajorVer;
					outbuf[i++] = (uint8_t) MinorVer;
					outbuf[i++] = (uint8_t) SubMinorVer;
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
					//APP_LOG(TS_OFF, VLEVEL_L, "###### YUNHORN Report Version [ %X ] \r\n", outbuf);
				}
				else
				if ((char)tlv_buf[CFG_CMD3] == 'C') { // "YVC"   REPORT NVM STORED CONFIG PARAMETERS  "YVC"

					uint8_t cfg_in_nvm[YUNHORN_STS_MAX_NVM_CFG_SIZE]="";
					OnRestoreSTSCFGContextRequest((uint8_t *)cfg_in_nvm);
					i=0;
					memset((void*)outbuf,0x0,sizeof(outbuf));

					outbuf[i++] = (uint8_t) 'C';
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_MTM1];  				//MTM Code
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_MTM2];  				//MTM Code
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_VER];  				//STS Version
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_HWV];  				//STS Version
					outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_PERIODICITY]);  	//UPLINK Periodicity
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_UNIT];  				//UPLINK Periodicity unit
					outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_SAMPLING]);  		//Heart-beat or SAMPLING Periodicity
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_S_UNIT];  			//Heart-beat or SAMPLING Periodicity unit
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_WORK_MODE];			// STS WORK MODE
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_SERVICE_MASK];  		//service mask
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_IOC_MASK];  		//service mask
					outbuf[i++] = (uint8_t) cfg_in_nvm[NVM_LEN];  				//length of following cfg value

					for (uint8_t j=0; j < cfg_in_nvm[NVM_LEN]; j++) {
						outbuf[i++] = (uint8_t) (cfg_in_nvm[NVM_CFG_START+j]);
					}
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

				}
			break;
								// "YO#","YF#","YH#","YD##L" ##={0,9} L={S,M,H}
		    case 'O':
						// TODO # of modules
						if (((uint8_t)(tlv_buf[CFG_CMD3]-0x30) >= 0) && ((uint8_t)tlv_buf[CFG_CMD3]-0x30) <=9)
						{
							STS_SENSOR_Power_ON((uint8_t)(tlv_buf[CFG_CMD3]-0x30));
							i=0;
							outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD1];
							outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD2];
							outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD3];
							STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
						} else {
							STS_SENSOR_Upload_Config_Invalid_Message();
						}
		    break;

		    case 'F':
					// TODO # of modules
						if (((uint8_t)(tlv_buf[CFG_CMD3]-0x30) >= 0) && ((uint8_t)tlv_buf[CFG_CMD3]-0x30) <=9) {
						STS_SENSOR_Power_OFF((tlv_buf[CFG_CMD3]-0x30));
						i=0;
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD3];
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;
			case 'H':
					// TODO # of modules
						if (((uint8_t)(tlv_buf[CFG_CMD3]-0x30) >= 0) && ((uint8_t)tlv_buf[CFG_CMD3]-0x30) <=9) {
						STS_SENSOR_MEMS_Reset((tlv_buf[CFG_CMD3]-0x30));
						i=0;
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] 	= (uint8_t) tlv_buf[CFG_CMD3];
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;

			case 'D':		// regular uploading message interval
				if ((((char)tlv_buf[CFG_CMD3] >= '0') && ((char)tlv_buf[CFG_CMD3] <='9')
					&& ((char)tlv_buf[CFG_CMD4] >='0') && ((char)tlv_buf[CFG_CMD4] <='9')) &&
						(((char)tlv_buf[CFG_CMD5] == 'M' || ((char)tlv_buf[CFG_CMD5] =='H') ||((char)tlv_buf[CFG_CMD5] =='S'))))
					{
						uint32_t periodicity_length = (tlv_buf[CFG_CMD3]-0x30)*10+ (tlv_buf[CFG_CMD4]-0x30);

						if ((char)tlv_buf[CFG_CMD5] == 'M') {
							periodicity_length *= 60;
						} else if ((char)tlv_buf[CFG_CMD5] == 'H') {
							periodicity_length *= 3600;
						}
						TxPeriodicity = periodicity_length*1000;	//translate to 1000ms=1s
						//HeartBeatPeriodicity = TxPeriodicity; 2024-05-22, fix issue of heart-beat interval mis-changed by Tx Interval

						OnTxPeriodicityChanged(TxPeriodicity);
						i = 0;
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD3];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD4];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD5];

						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
						// Save config to NVM
						sts_cfg_nvm.mtmcode1 = (uint8_t)sts_mtmcode1;
						sts_cfg_nvm.mtmcode2 = (uint8_t)sts_mtmcode2;
						sts_cfg_nvm.version = (uint8_t)sts_version;
						sts_cfg_nvm.hardware_ver = (uint8_t)sts_hardware_ver;
						sts_cfg_nvm.periodicity = (uint8_t)((tlv_buf[CFG_CMD3]-0x30)*10+(tlv_buf[CFG_CMD4]-0x30));
						sts_cfg_nvm.unit = (uint8_t)tlv_buf[CFG_CMD5];
						sts_cfg_nvm.work_mode = (uint8_t)sts_work_mode;
						sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;

						OnStoreSTSCFGContextRequest();
						APP_LOG(TS_OFF, VLEVEL_H, "###### YUNHORN Periodicity Changed to [ %d ] Seconds\r\n", periodicity_length);

					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;
			case 'S':					// SAMPLING INTERVAL OR DURATION
				if ((((char)tlv_buf[CFG_CMD3] >= '0') && ((char)tlv_buf[CFG_CMD3] <='9')
					&& ((char)tlv_buf[CFG_CMD4] >='0') && ((char)tlv_buf[CFG_CMD4] <='9')) &&
						(((char)tlv_buf[CFG_CMD5] == 'M' || ((char)tlv_buf[CFG_CMD5] =='H') ||((char)tlv_buf[CFG_CMD5] =='S'))))
					{
						uint32_t heart_beat_or_sampling_periodicity_length = (tlv_buf[CFG_CMD3]-0x30)*10+ (tlv_buf[CFG_CMD4]-0x30);

						if ((char)tlv_buf[CFG_CMD5] == 'M') {
							heart_beat_or_sampling_periodicity_length *= 60;
						} else if ((char)tlv_buf[CFG_CMD5] == 'H') {
							heart_beat_or_sampling_periodicity_length *= 3600;
						}

#ifdef YUNHORN_STS_E0_ENABLED
						SamplingPeriodicity = heart_beat_or_sampling_periodicity_length*1000;  //translate to 1000ms=1s
						OnYunhornSTSSamplingPeriodicityChanged(SamplingPeriodicity);
#endif
#if defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)
						HeartBeatPeriodicity = heart_beat_or_sampling_periodicity_length*1000;  //translate to 1000ms=1s
						OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);
#endif
						i = 0;
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD1];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD2];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD3];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD4];
						outbuf[i++] = (uint8_t) tlv_buf[CFG_CMD5];

						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
						// Save config to NVM
						sts_cfg_nvm.mtmcode1 = (uint8_t)sts_mtmcode1;
						sts_cfg_nvm.mtmcode2 = (uint8_t)sts_mtmcode2;
						sts_cfg_nvm.version = (uint8_t)sts_version;
						sts_cfg_nvm.hardware_ver = (uint8_t)sts_hardware_ver;
						sts_cfg_nvm.sampling = (uint8_t)((tlv_buf[CFG_CMD3]-0x30)*10+(tlv_buf[CFG_CMD4]-0x30));
						sts_cfg_nvm.s_unit = (uint8_t)tlv_buf[CFG_CMD5];
						sts_cfg_nvm.work_mode = (uint8_t)sts_work_mode;
						sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;

						OnStoreSTSCFGContextRequest();
						APP_LOG(TS_OFF, VLEVEL_H, "###### YUNHORN Sampling Or Heart-Beat Interval Changed to [ %d ] Seconds\r\n", heart_beat_or_sampling_periodicity_length);

					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;


			default:
					//STS_SENSOR_Upload_Config_Invalid_Message();
			break;
		}   // END OF switch switch ((char)tlv_buf[CFG_CMD2])
	} 	// end of if    END OF *** BOARD LEVEL CONTROL OR REPORT
	else if (((char)tlv_buf[CFG_CMD1] == 'P') && (tlv_buf_size >= 3)) //   BEGIN OF PARAMETER CONFIG
	{
	 /*
	  * YUNHORN STS PRODUCT SUBMODULE, MEMS OR SENSOR HEAD LEVEL PARAMETER TUNING SECTION
	  */

		//i = P_MEM_CFG;		//start of parameter

		switch ((char)tlv_buf[CFG_CMD2])   //BEGIN OF SWITCH TVL_BUF_P_MEMS_NO
		{   //#1 No. of MEMS components
			//default first sensor head or MEMS component, default 1 sensor heads
			case '0':					//default sensor head or MEMS component
			case '1':					//first sensor head or MEMS component

				mems_ver = (uint8_t)(tlv_buf[CFG_CMD3]-0x30);

				uint8_t j=0;
				if (mems_ver == sts_version)
				{					// Firmware version or Variation of MEMS/component
					if (tlv_buf_size >= CFG_CMD_TOF_SIMPLE_SIZE)
					{
							//Validation check
							invalid_flag = 0;

							for (j =0; j < CFG_CMD_TOF_SIMPLE_SIZE; j++)
							{
								sts_cfg_nvm.p[j] = (uint8_t)((tlv_buf[CFG_CMD4+j] - 0x30)&0xff);
								APP_LOG(TS_OFF,VLEVEL_H,"\r\n tlv_buf %d = %02x cfg->p[%d]=%02x \r\n",
										j,tlv_buf[CFG_CMD4+j], j, sts_cfg_nvm.p[j]);
							}


							//STS_PRESENCE_SENSOR_NVM_CFG_SIMPLE();
							STS_SENSOR_NVM_CFG_SIMPLE();

							i=0;						// Step 1: Prepare status update message
							outbuf[i++] 	= (char) 'P';
							outbuf[i++] 	= sts_mtmcode1;
							outbuf[i++] 	= sts_mtmcode2;
							outbuf[i++] 	= sts_version;
							outbuf[i++] 	= sts_hardware_ver;

							for (j=0; j < CFG_CMD_TOF_SIMPLE_SIZE; j++)
							{
								outbuf[i++] = (uint8_t)(sts_cfg_nvm.p[j]+0x30)&0xff;
							}
							//APP_LOG(TS_OFF, VLEVEL_H, "###### RSS Simple CFG=%s\r\n",outbuf);

							// Step 2: Save valid config to NVM
							sts_cfg_nvm.mtmcode1 = sts_mtmcode1;
							sts_cfg_nvm.mtmcode2 = sts_mtmcode2;
							sts_cfg_nvm.version = sts_version;
							sts_cfg_nvm.hardware_ver = sts_hardware_ver;
							sts_cfg_nvm.work_mode = sts_work_mode;
							sts_cfg_nvm.sts_service_mask = sts_service_mask;
							sts_cfg_nvm.length = CFG_CMD_TOF_SIMPLE_SIZE;

							OnStoreSTSCFGContextRequest();
					}

					// Invalid parameters
					// Step 1/2: Prepare status update message
				if (invalid_flag == 1)
				{
					STS_SENSOR_Upload_Config_Invalid_Message();
					//APP_LOG(TS_OFF, VLEVEL_L, "###### MTM VER Invalid or Mismatch\r\n");
				}

				// Step 3: Upload status update message
				STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

				}

			break;

			// for multiple sensor heads or MEMS components, TODO   2022-10-21 PARK HERE

			case 2:
			#ifdef YUNHORN_STS_O2_ENABLED
				// for 2nd sensor heads such as 2x Presence radar, 2 reed-switch or hall elements
			#endif

			break;
			default:
			// for multiple sensor heads or MEMS components, TODO   2022-10-21 PARK HERE
				//STS_SENSOR_Upload_Config_Invalid_Message();
			break;
		}   //END OF SWITCH TVL_BUF_P_MEMS_NO

	} //END  OF PARAMETER CONFIG
	else if (((char)tlv_buf[CFG_CMD1] == 'A') && ((char)tlv_buf[CFG_CMD2] == 'C') && (tlv_buf_size == (YUNHORN_STS_AC_CODE_SIZE+2)))  // BEGIN OF *** BOARD LEVEL AUTHORIZATION CODE
		{
			// 'AC'+ AC_CODE(20bytes)

			for (uint8_t j=0; j< YUNHORN_STS_AC_CODE_SIZE; j++)
			{
				sts_ac_code[j] = (uint8_t) tlv_buf[2+j];
			}

			STS_YUNHORN_RFAC_HANDLE_PROCESS();

			if ((hmac_result.ac_pass == 1U))
			{
				for (uint8_t j=0; j < YUNHORN_STS_AC_CODE_SIZE; j++)
				{
					sts_cfg_nvm.ac[j] = sts_ac_code[j];
				}

				sts_service_mask = STS_SERVICE_MASK_L0;

				sts_cfg_nvm.sts_service_mask = sts_service_mask;
				OnStoreSTSCFGContextRequest();


			} else {
				sts_service_mask = STS_SERVICE_MASK_L2;

			}

			i=0;
			for (uint8_t j=0; j <(tlv_buf_size) ; j++) {
				outbuf[i++] = (uint8_t) tlv_buf[j];
			}
			STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

		}

} // END OF  USER_APP_AUTO_RESPONDER_Parse

void STS_SENSOR_Upload_Config_Invalid_Message(void)
{
	if (sts_service_mask == STS_SERVICE_MASK_L0)
		STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, 5, (uint8_t *)"PVXXX");
}

static void STS_YUNHORN_RFAC_HANDLE_PROCESS(void)
{

	STS_YunhornAuthenticationCode_Process();

}

void STS_SENSOR_Upload_Message(uint8_t appDataPort, uint8_t appBufferSize, uint8_t *appDataBuffer)
{
	LmHandlerErrorStatus_t status = LORAMAC_HANDLER_ERROR;
	UTIL_TIMER_Time_t nextTxIn = 0;

	if (LmHandlerIsBusy() == false)
	{

		for (uint8_t i=0;i<appBufferSize; i++) {
			AppData.Buffer[i] = appDataBuffer[i];
		}

		AppData.Port = appDataPort;
		AppData.BufferSize = (sts_service_mask >1?0:appBufferSize);

	    if ((JoinLedTimer.IsRunning) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET))
	    {
	      UTIL_TIMER_Stop(&JoinLedTimer);
#if	defined(STM32WL55xx)
	      HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET); /* LED_RED */
#endif
	    }

		status = LmHandlerSend(&AppData, LmHandlerParams.IsTxConfirmed, false);
		if (LORAMAC_HANDLER_SUCCESS == status)
		{
			APP_LOG(TS_ON, VLEVEL_H, "SEND REQUEST\r\n");
		}
		else if (LORAMAC_HANDLER_DUTYCYCLE_RESTRICTED == status)
		{
			nextTxIn = LmHandlerGetDutyCycleWaitTime();
			if (nextTxIn > 0)
			{
				APP_LOG(TS_ON, VLEVEL_H, "Next Tx in  : ~%d second(s)\r\n", (nextTxIn / 1000));
			}
		}

	}

	if (EventType == TX_ON_TIMER)
	{
	    UTIL_TIMER_Stop(&TxTimer);
	    UTIL_TIMER_SetPeriod(&TxTimer, MAX(nextTxIn, TxPeriodicity));
	    UTIL_TIMER_Start(&TxTimer);
	}
}


void OnStoreSTSCFGContextRequest(void)
{
  /* USER CODE BEGIN OnStoreContextRequest_1 */
	uint8_t i=0,j=0,nvm_store_value[YUNHORN_STS_MAX_NVM_CFG_SIZE]={0x0};

#if (defined(YUNHORN_STS_O5_ENABLED) || defined(YUNHORN_STS_O6_ENABLED) || defined(YUNHORN_STS_R0_ENABLED) || defined(YUNHORN_STS_R4_ENABLED) || defined(YUNHORN_STS_R5_ENABLED))
	sts_cfg_nvm.length   = STS_NVM_CFG_SIZE;
	nvm_store_value[i++] = sts_cfg_nvm.mtmcode1;
	nvm_store_value[i++] = sts_cfg_nvm.mtmcode2;
	nvm_store_value[i++] = sts_cfg_nvm.version;
	nvm_store_value[i++] = sts_cfg_nvm.hardware_ver;
	nvm_store_value[i++] = sts_cfg_nvm.periodicity;
	nvm_store_value[i++] = sts_cfg_nvm.unit;
	nvm_store_value[i++] = sts_cfg_nvm.sampling;
	nvm_store_value[i++] = sts_cfg_nvm.s_unit;
	nvm_store_value[i++] = sts_cfg_nvm.work_mode;
	nvm_store_value[i++] = sts_cfg_nvm.sts_service_mask;
	nvm_store_value[i++] = sts_cfg_nvm.reseve01;
	nvm_store_value[i++] = (uint8_t) STS_NVM_CFG_SIZE; //sts_cfg_nvm.length;

	for (j = 0; j < STS_CFG_PCFG_SIZE; j++) {
		nvm_store_value[i++] = (sts_cfg_nvm.p[j]);
	}

	nvm_store_value[i++] = sts_cfg_nvm.fall_detection_acc_threshold;
	nvm_store_value[i++] = sts_cfg_nvm.fall_detection_depth_threshold;
	nvm_store_value[i++] = sts_cfg_nvm.fall_confirm_threshold_in_10sec;
	//nvm_store_value[i++] = sts_cfg_nvm.occupancy_overtime_threshold;
	if ((sts_cfg_nvm.ac[0]!=0x0) && (sts_cfg_nvm.ac[19]!=0x0)) {
		for (j = 0; j < YUNHORN_STS_AC_CODE_SIZE; j++) {
			nvm_store_value[i++] = (sts_cfg_nvm.ac[j]);
		}
	}
#endif
  /* USER CODE END OnStoreContextRequest_1 */
  /* store nvm in flash */

  if (FLASH_IF_Erase(STS_CONFIG_NVM_BASE_ADDRESS, FLASH_PAGE_SIZE) == FLASH_IF_OK)
  {
    FLASH_IF_Write(STS_CONFIG_NVM_BASE_ADDRESS, (const void *)nvm_store_value, YUNHORN_STS_MAX_NVM_CFG_SIZE);
  }


  /* USER CODE BEGIN OnStoreContextRequest_Last */


  /* USER CODE END OnStoreContextRequest_Last */
}

void OnRestoreSTSCFGContextRequest(uint8_t *cfg_in_nvm)
{
  /* USER CODE BEGIN OnRestoreSTSCFGContextRequest_1 */

  /* USER CODE END OnRestoreSTSCFGContextRequest_1 */
  UTIL_MEM_cpy_8(cfg_in_nvm, (void *)STS_CONFIG_NVM_BASE_ADDRESS, YUNHORN_STS_MAX_NVM_CFG_SIZE);
  /* USER CODE BEGIN OnRestoreSTSCFGContextRequest_Last */

  /* USER CODE END OnRestoreSTSCFGContextRequest_Last */
}


void STS_REBOOT_CONFIG_Init(void)
{
  /* USER CODE BEGIN OnRestoreContextRequest_1 */
	uint8_t nvm_stored_value[YUNHORN_STS_MAX_NVM_CFG_SIZE]={0x0};

  /* USER CODE END OnRestoreContextRequest_1 */
	UTIL_MEM_cpy_8(nvm_stored_value, (void *)STS_CONFIG_NVM_BASE_ADDRESS, YUNHORN_STS_MAX_NVM_CFG_SIZE);
  /* USER CODE BEGIN OnRestoreContextRequest_Last */

#if defined(YUNHORN_STS_O6_ENABLED) || defined(YUNHORN_STS_R0_ENABLED) || defined(YUNHORN_STS_R4_ENABLED) || defined(YUNHORN_STS_R5_ENABLED)
  if ((nvm_stored_value[NVM_MTM1] != sts_mtmcode1) || (nvm_stored_value[NVM_MTM2] != sts_mtmcode2) || (nvm_stored_value[NVM_VER] != sts_version))
	{
		APP_LOG(TS_OFF, VLEVEL_H, "\r\nInitial Boot with Empty Config, Flash with default config....\r\n");
		OnStoreSTSCFGContextRequest();
		//UTIL_MEM_set_8((void *)sts_ac_code, 0x00, YUNHORN_STS_AC_CODE_SIZE);
		HAL_Delay(1000);
	} else
	{
		sts_cfg_nvm.mtmcode1 			= (uint8_t)nvm_stored_value[NVM_MTM1];
		sts_cfg_nvm.mtmcode2 			= (uint8_t)nvm_stored_value[NVM_MTM2];
		sts_cfg_nvm.version 			= (uint8_t)nvm_stored_value[NVM_VER];
		sts_cfg_nvm.hardware_ver  		= (uint8_t)nvm_stored_value[NVM_HWV];
		sts_cfg_nvm.periodicity 		= (uint8_t)(nvm_stored_value[NVM_PERIODICITY]);				//TxPeriodicity interval
		sts_cfg_nvm.unit				= (uint8_t)(nvm_stored_value[NVM_UNIT]);
		sts_cfg_nvm.sampling			= (uint8_t)(nvm_stored_value[NVM_SAMPLING]);				//Heart-beat or sampling interval
		sts_cfg_nvm.s_unit				= (uint8_t)(nvm_stored_value[NVM_S_UNIT]);
		sts_cfg_nvm.work_mode			= (uint8_t)(nvm_stored_value[NVM_WORK_MODE]);
		sts_cfg_nvm.sts_service_mask	= (uint8_t)(nvm_stored_value[NVM_SERVICE_MASK]);
		sts_cfg_nvm.reseve01			= (uint8_t)(nvm_stored_value[NVM_IOC_MASK]);
		sts_cfg_nvm.length 				= (uint8_t)(nvm_stored_value[NVM_LEN]&0x3F);		//MAX 32 bytes

		for (uint8_t j=0; j< sts_cfg_nvm.length; j++) {
			sts_cfg_nvm.p[j] 				= (uint8_t)nvm_stored_value[NVM_CFG_START+j];
		}

		sts_cfg_nvm.fall_detection_acc_threshold 	= (uint8_t)nvm_stored_value[NVM_FALL_DETECTION_ACC_THRESHOLD];
		sts_cfg_nvm.fall_detection_depth_threshold 	= (uint8_t)nvm_stored_value[NVM_FALL_DETECTION_DEPTH_THRESHOLD];
		sts_cfg_nvm.fall_confirm_threshold_in_10sec	= (uint8_t)nvm_stored_value[NVM_FALL_CONFIRM_THRESHOLD];
		//sts_cfg_nvm.occupancy_overtime_threshold 	= (uint8_t)nvm_stored_value[NVM_OCCUPANCY_OVERTIME_THRESHOLD];

		for (uint8_t j=0; j< YUNHORN_STS_AC_CODE_SIZE; j++) {
			sts_cfg_nvm.ac[j] 				= (uint8_t)nvm_stored_value[NVM_AC_CODE_START +j];
		}
	}
#endif

	OnRestoreSTSCFGContextProcess();

  /* USER CODE END OnRestoreContextRequest_Last */
}

void OnRestoreSTSCFGContextProcess(void)
{
	uint32_t periodicity = (sts_cfg_nvm.periodicity);		//TxPeriodicty interval
	if ((char)sts_cfg_nvm.unit =='M') {
		periodicity *= 60;
	} else if ((char) sts_cfg_nvm.unit =='H') {
		periodicity *= 3600;
	} else if ((char) sts_cfg_nvm.unit =='S') {
		periodicity *= 1;
	}
	periodicity = (periodicity > 10)? periodicity : 10;		// in seconds unit
	TxPeriodicity= periodicity*1000;  // to ms

	uint32_t samplingperiodicity = (sts_cfg_nvm.sampling);		//Heart-beat or Sampling interval
	if ((char)sts_cfg_nvm.s_unit =='M') {
		samplingperiodicity *= 60;
	} else if ((char) sts_cfg_nvm.s_unit =='H') {
		samplingperiodicity *= 3600;
	} else if ((char) sts_cfg_nvm.s_unit =='S') {
		samplingperiodicity *= 1;
	}

	if ((sts_cfg_nvm.ac[0] ==0x0 )&& (sts_cfg_nvm.ac[19]==0x0))
	{															// ensure it's not in production yet
		OnTxPeriodicityChanged(APP_TX_DUTYCYCLE);				// in msec unit
		OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);
	} else
	{
		OnTxPeriodicityChanged(TxPeriodicity);				// in msec unit
		//Heart-beat or Sampling interval
		samplingperiodicity = (samplingperiodicity > 0)? samplingperiodicity : 1;		// in seconds unit
		HeartBeatPeriodicity = samplingperiodicity*1000;
#if	defined(YUNHORN_STS_O6_ENABLED) ||defined(YUNHORN_STS_O5_ENABLED)
	OnYunhornSTSSamplingPeriodicityChanged(HeartBeatPeriodicity);				// in m-sec unit
#endif

#if	defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)||defined(YUNHORN_STS_R4_ENABLED)
	OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);
#endif
	}

	sts_work_mode	= sts_cfg_nvm.work_mode;
	sts_service_mask = sts_cfg_nvm.sts_service_mask;

#ifdef YUNHORN_STS_O6_ENABLED
	sts_lamp_bar_color = STS_GREEN;
	sts_fall_detection_acc_threshold 	= (uint8_t)sts_cfg_nvm.fall_detection_acc_threshold*10;
	sts_fall_detection_depth_threshold 	= (uint8_t)sts_cfg_nvm.fall_detection_depth_threshold*10;		//in cm
	// ****								= sts_cfg_nvm.fall_detection_reserve;
	sts_occupancy_overtime_threshold 	= (uint8_t)sts_cfg_nvm.occupancy_overtime_threshold*10;			// minutes
#endif


	for (uint8_t j=0; j< YUNHORN_STS_AC_CODE_SIZE; j++)
	{
		sts_ac_code[j] = sts_cfg_nvm.ac[j];
	}

#ifdef YUNHORN_STS_O6_ENABLED
	if ((sts_version == sts_cfg_nvm.version)&& (NVM_CFG_PARAMETER_SIZE == sts_cfg_nvm.length))
	{
		STS_PRESENCE_SENSOR_Init();
		STS_PRESENCE_SENSOR_RSS_Init();
	}
#endif

}

void STS_SENSOR_Distance_Test_Process(void)
{
#ifdef	YUNHORN_STS_O6_ENABLED
	sts_distance_rss_distance =0;
	do {
		STS_PRESENCE_SENSOR_Distance_Measure_Process();
		HAL_Delay(200);
	} while(sts_distance_rss_distance == 0);

	APP_LOG(TS_OFF, VLEVEL_M, "\r\nSensor Function Test: Distance Measured =%u mm\r\n", (int)sts_distance_rss_distance);
#endif

#if	defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)
		MX_TOF_Init();
		MX_TOF_Process();
#endif
}

void STS_SENSOR_Function_Test_Process(void)
{
	char tstbuf[128] =""; uint8_t i=0;
	uint8_t count = 0;
	uint8_t mems_Dev_ID[2] = {0x0,0x0};

	tstbuf[i++] = (uint8_t) 'S';
	tstbuf[i++] = (uint8_t) sts_mtmcode1;
	tstbuf[i++] = (uint8_t) sts_mtmcode2;
	tstbuf[i++] = (uint8_t) sts_version;
	tstbuf[i++] = (uint8_t) sts_hardware_ver;
	tstbuf[i++] = (uint8_t) (99*GetBatteryLevel()/254)&0xff;


	count = STS_SENSOR_MEMS_Get_ID(mems_Dev_ID);

//	if ((mems_Dev_ID[0]==0X0) && (mems_Dev_ID[1]==0x0))
	if (count ==0)
	{
		tstbuf[i++] = (uint8_t) 'X'; // Slave MEMS Not Avaliable
	}
	else
	{
		//tstbuf[i++] = (uint8_t) (count*2+1)&0xff;	//length of following data
#ifdef YUNHORN_STS_O6_ENABLED
		tstbuf[i++] = (uint8_t)20; //length of following data
		uint8_t self_test_result[10]={0,0,0,0,0, 0,0,0,0,0};

		STS_PRESENCE_SENSOR_Function_Test_Process(&self_test_result[0], count);

		for (uint8_t j=0; j < 10; j++)
			tstbuf[i++] = (uint8_t) (self_test_result[j])&0xff;

		STS_PRESENCE_SENSOR_Distance_Measure_Process();

		tstbuf[i++] = (uint8_t) ((((uint16_t)sts_distance_rss_distance)/1000)%10+0x30)&0xff;
		tstbuf[i++] = (uint8_t) ((((uint16_t)sts_distance_rss_distance)/100)%10+0x30)&0xff;
		tstbuf[i++] = (uint8_t) ((((uint16_t)sts_distance_rss_distance)/10)%10+0x30)&0xff;
		tstbuf[i++] = (uint8_t) (((uint16_t)sts_distance_rss_distance)%10+0x30)&0xff;
#endif

#if	(defined(YUNHORN_STS_R0_ENABLED)||defined(YUNHORN_STS_R5_ENABLED)||defined(YUNHORN_STS_R1_ENABLED))
		tstbuf[i++] = (uint8_t) (count)&0xff;
		//MX_TOF_Process();
		MX_TOF_Init();
		STS_TOF_VL53L0X_Range_Process();

#ifdef	TOF_1
		tstbuf[i++] = (uint8_t) ((sts_tof_distance_data[0] >>8 ) &0xff);
		tstbuf[i++] = (uint8_t) (sts_tof_distance_data[0]  &0xff);
#endif
#ifdef	TOF_2
		tstbuf[i++] = (uint8_t) ((sts_tof_distance_data[1] >>8 ) &0xff);
		tstbuf[i++] = (uint8_t) (sts_tof_distance_data[1]  &0xff);
#endif
#ifdef	TOF_3
		tstbuf[i++] = (uint8_t) ((sts_tof_distance_data[2] >>8 ) &0xff);
		tstbuf[i++] = (uint8_t) (sts_tof_distance_data[2]  &0xff);
#endif

#endif
	}
	memset((void*)outbuf,0x0,sizeof(outbuf));
	memcpy((void*)outbuf, tstbuf, i);

	STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

}