/* 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"
#include "main.h"
#ifdef	CLOCK_SYNC
#include "LmhpClockSync.h"
#endif
/* USER CODE BEGIN Includes */
#include "tim.h"
#include "sts_lamp_bar.h"
#include "yunhorn_sts_sensors.h"
#include "sts_cmox_hmac_sha.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;

extern volatile uint8_t sts_hall1_read, sts_hall2_read, sts_hall3_read, sts_hall4_read;
volatile uint8_t last_sts_hall1_read=STS_Status_Door_Open, last_sts_hall2_read=STS_Status_SOS_Release, last_sts_hall3_read=STS_Status_Door_Open, last_sts_hall4_read=STS_Status_Door_Open ;

volatile uint8_t sts_reed_hall_result =0, sts_emergency_button_pushed=0;	// inital 0 = close
extern volatile uint8_t sts_work_mode, sts_cloud_netcolor, sts_lamp_bar_color, sts_status_color;
extern volatile uint8_t sts_lamp_bar_flashing_color;
volatile uint8_t last_sts_lamp_bar_color=STS_DARK;
extern volatile uint8_t sts_rss_result;
extern volatile uint8_t sts_rss_result_changed_flag, sts_hall1_changed_flag, sts_hall2_changed_flag, sts_reed_hall_changed_flag;
volatile uint8_t sts_fall_detection_acc_threshold = 30, //0.3g
		sts_fall_detection_depth_threshold=20,
		sts_fall_confirm_threshold_in_10sec=1,
		sts_occupancy_overtime_threshold_in_10min=2;
volatile uint8_t sts_unconscious_or_motionless_level_threshold=6; //6*128
volatile uint8_t sts_motionless_duration_threshold_in_min=1; // test mode, 1 min, normal 10 min. long occupation 30 min
volatile uint16_t sts_unconscious_level_threshold=600;
volatile uint8_t sts_alarm_mute_reset_timer_in_10sec=6;
extern volatile uint8_t sts_fall_rising_detected_result;
extern volatile uint32_t event_start_time, event_stop_time;
extern volatile uint16_t sts_unconscious_threshold;
volatile uint8_t sts_occupancy_overtime_state = 0;
volatile uint8_t sts_presence_fall_detection=TRUE;
volatile uint32_t SamplingPeriodicity = 3000; 		//unit ms
volatile uint32_t HeartBeatPeriodicity = 120000; 		//unit ms
volatile uint8_t STS_LoRa_WAN_Joined = 0;

volatile uint8_t upload_message_timer=0;
volatile uint8_t heart_beat_timer =0;
volatile uint32_t wcnt=0;
volatile bool p2_work_finished=true;
extern volatile uint8_t luminance_level;
uint8_t outbuf[255]={0x0};
volatile static bool r_b=true;
volatile sts_cfg_nvm_t sts_cfg_nvm = {
		sts_mtmcode1,
		sts_mtmcode2,
		sts_version,
		sts_hardware_ver,
		0x05,
		'M', //Uplink data interval          for heart-beat uplink
		0x03,
		'S', //Sampling sensor interval      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
		0x00,		//intra frame weight, 0x00=[0]*0.1=0.0F
		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}
};

#if	defined(STS_O6)||defined(STS_O7)
extern volatile uint8_t sensor_data_ready;
extern volatile STS_OO_SensorStatusDataTypeDef sts_o7_sensorData;
extern volatile uint16_t sts_distance_rss_distance, sts_sensor_install_height;
extern volatile uint8_t sts_rss_config_updated_flag;
//#define STS_Status_Door_Close                (1)		//Normal Close NC:Open		**2024-07-15 changed
//#define STS_Status_Door_Open                 (0)		//Normal Close NC:Close		**2024-07-15 changed

char sts_door_status_code[2][10]={
	"Close",
	"Open "
};
char sts_sos_status_code[2][10]={
	"PushDown",
	"No_Push"
};
char sts_lamp_color_code[15][15]={
	"Dark",
	"Green",
	"Red",
	"Blue",
	"Yellow",
	"Pink",
	"Cyan",
	"White",
	"color_max", //8
	"Green_Flash", //0x10 ==16
	"Red_Flash",  // 0x20 ==32
	"Blue_Flash", // 0x30 == 48
	"Red_Blue_Flash" //0x23 = 35
};

char sts_work_mode_code[15][25] ={
	"NETWORK_MODE",
	"WIRED_MODE",
	"REEDSWITCH_MODE",
	"RSS_MODE",
	"DUAL_MODE",
	"UNI_MODE",
	"REMOTE_REED_RSS_MODE",
	"DUAL_RSS_MODE",
	"TOF_RSS_MODE",
	"TOF_DISTANCE_MODE",
	"TOF_PRESENCE_MODE",
	"TOF_IN_OUT_MODE",
	"FALL_DETECTION_MODE",
	"OTHER_MODE1",
	"OTHER_MODE2",
};
char sts_fall_mode_code[8][25] = {
		"State_Normal",
		"State_Fall_Down",
		"State_Rising_Up",
		"State_Laydown",
		"State_Unconscious",
		"State_StayStill",
		"State_No_Movement"
};
#endif
/* 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_RSS_WAKEUP_CHECK_TIME 		SamplingPeriodicity		//3000 ms
#define YUNHORN_STS_SAMPLING_CHECK_TIME			SamplingPeriodicity
#define YUNHORN_STS_HEART_BEAT_CHECK_TIME		300000					//300 sec, 5 min
/* 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  STS Lamp Bar timer callback function
  * @param  context ptr of Lamp Bar LED context
  */
static void OnYunhornSTSLampBarColorTimerEvent(void *context);

/**
  * @brief  SYS occupancy, door lock, motion duration check timer callback function
  * @param  context ptr of duration check context
  */
static void OnYunhornSTSDurationCheckTimerEvent(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 timer callback function
  * @param  context ptr of context
  */

static void OnYunhornSTSHeartBeatTimerEvent(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 AC Code 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; //TX_ON_TIMER;//TX_ON_EVENT;

/**
  * @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;

static volatile bool IsClockSynched = false;
/**
  * @brief Timer to handle the Yunhorn STS Lamp Bar Color Led to toggle
  */
static UTIL_TIMER_Object_t STSLampBarColorTimer;

/**
  * @brief Timer to handle the Yunhorn STS Lamp Bar Color Led to toggle
  */
static UTIL_TIMER_Object_t STSDurationCheckTimer;

/**
  * @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 Heart Beat Process
  */
static UTIL_TIMER_Object_t YunhornSTSHeartBeatTimer;

/* 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_M, "\r\n\n\n##### YUNHORN_STS_SWV%d HWV:%d MTM:%d.%d R:%d.%d.%d####\r\n\n\n",
	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);
  UTIL_TIMER_Create(&STSLampBarColorTimer, LED_PERIOD_TIME, UTIL_TIMER_ONESHOT, OnYunhornSTSLampBarColorTimerEvent, NULL);
  UTIL_TIMER_Create(&STSDurationCheckTimer, 20*LED_PERIOD_TIME, UTIL_TIMER_PERIODIC, OnYunhornSTSDurationCheckTimerEvent, 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);

  // TODO XXX  2024-06-04
#ifdef	CLOCK_SYNC
    LmHandlerPackageRegister( PACKAGE_ID_CLOCK_SYNC, NULL );
    IsClockSynched = false;
#endif
  /* 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 */
  /// **************************************************************************** TO-DO LIST
  STS_REBOOT_CONFIG_Init();

  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventRFAC), UTIL_SEQ_RFU, STS_YunhornSTSEventRFAC_Process);
  UTIL_SEQ_RegTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), UTIL_SEQ_RFU, STS_YunhornSTSEventP1_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);

#if 0
  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);
#endif

#if	defined(STS_O7)||defined(STS_O6)
  //UTIL_TIMER_Create(&YunhornSTSRSSWakeUpTimer, YUNHORN_STS_RSS_WAKEUP_CHECK_TIME, UTIL_TIMER_ONESHOT, OnYunhornSTSOORSSWakeUpTimerEvent, NULL);
  //UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);

  UTIL_TIMER_Create(&YunhornSTSHeartBeatTimer, YUNHORN_STS_HEART_BEAT_CHECK_TIME, UTIL_TIMER_PERIODIC, OnYunhornSTSHeartBeatTimerEvent, NULL);
  //UTIL_TIMER_Start(&YunhornSTSHeartBeatTimer);

  UTIL_TIMER_Start(&STSLampBarColorTimer);

  UTIL_TIMER_Start(&STSDurationCheckTimer);

#else
  UTIL_TIMER_Create(&YunhornSTSSamplingCheckTimer, YUNHORN_STS_SAMPLING_CHECK_TIME, UTIL_TIMER_PERIODIC, OnYunhornSTSSamplingCheckTimerEvent, NULL);
    UTIL_TIMER_Start(&YunhornSTSSamplingCheckTimer);
#endif
  /* USER CODE END LoRaWAN_Init_Last */
}

/* USER CODE BEGIN PB_Callbacks */

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  switch (GPIO_Pin)
  {
#if	defined(STS_O6)||defined(STS_O7)
  case  HALL1_Pin:

	  sts_hall1_read = HALL1_STATE;
	  HAL_Delay(30);	//de-bouncing

	  if ((sts_hall1_read == HALL1_STATE))	// && (sts_hall1_read == STS_Status_Door_Close)) 2024-07-22 only process door close event
	  {
		  APP_LOG(TS_OFF, VLEVEL_L, "\n\n Door Contact Read = %02x  --%s\r\n", sts_hall1_read, sts_door_status_code[sts_hall1_read]);
		  OnSensor1StateChanged();
		  /* Note: when "EventType == TX_ON_TIMER" this GPIO is not initialized */
		  //	if (EventType == TX_ON_EVENT)
		  {
			  STS_YunhornSTSEventP1_Process();
			  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
			  //SendTxData();
		  }
			  last_sts_hall1_read = sts_hall1_read;
	  }
  break;

  case  HALL2_Pin:
	  sts_hall2_read = HALL2_STATE;
	  HAL_Delay(30);	//de-bouncing
	  if (sts_hall2_read == HALL2_STATE)
	  {
		  APP_LOG(TS_OFF, VLEVEL_L, "\n\n SOS Button Read = %02x   --%s\r\n", sts_hall2_read, sts_sos_status_code[sts_hall2_read]);
		  OnSensor2StateChanged();
		  //sensor_data_ready =1;
		  //UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), CFG_SEQ_Prio_0);
		  {
			  STS_YunhornSTSEventP1_Process();
			  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);

		  }
				  last_sts_hall2_read = sts_hall2_read;
	  }
    break;

  case  HALL3_Pin:
	  sts_hall3_read = HALL3_STATE;
	  HAL_Delay(30);	//de-bouncing
	  if (sts_hall3_read == HALL3_STATE)
	  {
		  APP_LOG(TS_OFF, VLEVEL_L, "\n\n ALARM MUTE Button Read = %02x   --%s\r\n", sts_hall3_read, sts_sos_status_code[sts_hall3_read]);
		  OnSensor3StateChanged();
		  //sensor_data_ready =1;
		  //UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), CFG_SEQ_Prio_0);
		  {
			  STS_YunhornSTSEventP1_Process();
			  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);

		  }
				  last_sts_hall3_read = sts_hall3_read;
	  }
    break;

  case  HALL4_Pin:
	  sts_hall4_read = HALL4_STATE;
	  HAL_Delay(30);	//de-bouncing
	  if (sts_hall4_read == HALL4_STATE)
	  {
		  APP_LOG(TS_OFF, VLEVEL_L, "\n\n ALARM RESET Button Read = %02x   --%s\r\n", sts_hall4_read, sts_sos_status_code[sts_hall4_read]);
		  OnSensor4StateChanged();
		  //sensor_data_ready =1;
		  //UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP1), CFG_SEQ_Prio_0);
		  {
			  STS_YunhornSTSEventP1_Process();
			  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);

		  }
				  last_sts_hall4_read = sts_hall4_read;
	  }
    break;

#else
    case  BUT1_Pin:
    	HAL_Delay(20);	//de-bouncing

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

    /* Note: when "EventType == TX_ON_TIMER" this GPIO is not initialized */
    //	if (EventType == TX_ON_EVENT)
    {
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
    }
      break;

    case  BUT2_Pin:
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStopJoinEvent), CFG_SEQ_Prio_0);
      break;

    case  BUT3_Pin:
      UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaStoreContextEvent), CFG_SEQ_Prio_0);
      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)
  {
#ifndef	STM32WLE5xx
    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");
#ifndef	STM32WLE5xx
                  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");
#ifndef	STM32WLE5xx
                  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 = GetBatteryLevel();
  //sensor_t sensor_data;
  STS_OO_SensorStatusDataTypeDef sensorData;
  UTIL_TIMER_Time_t nextTxIn = 0;

  if (LmHandlerIsBusy() == false)
  {
	uint32_t i = 0;
	APP_LOG(TS_OFF, VLEVEL_M, "\r\n STS_WORK_MODE = %02x \r\n", sts_work_mode);
    STS_PRESENCE_SENSOR_Prepare_Send_Data(&sensorData);
    if (sts_work_mode == STS_DUAL_MODE)
    {
    	AppData.Port = (uint8_t)YUNHORN_STS_O6_LORA_APP_DATA_PORT;

    } else if (sts_work_mode == STS_UNI_MODE)
    {
    	 AppData.Port = (uint8_t)YUNHORN_STS_O7_LORA_APP_DATA_PORT;
    } else if ((sts_work_mode == STS_RSS_MODE)||(sts_work_mode == STS_REEDSWITCH_MODE)||(sts_work_mode == STS_NETWORK_MODE)||(sts_work_mode == STS_WIRED_MODE))
    {
    	AppData.Port = (uint8_t)YUNHORN_STS_O2_LORA_APP_DATA_PORT;
    }

    AppLedStateOn = LED1_STATE;

	if ((heart_beat_timer != 0L))			// sensor data OVERWRITE heart-beat message, 2024-05-12
	{
		heart_beat_timer=0;
		if (sts_work_mode == STS_DUAL_MODE)
			AppData.Port = (uint8_t)YUNHORN_STS_O6_LORA_APP_HTBT_PORT; //LORAWAN_USER_APP_PORT+1;
		else if (sts_work_mode == STS_UNI_MODE)
			AppData.Port = (uint8_t)YUNHORN_STS_O7_LORA_APP_HTBT_PORT; //LORAWAN_USER_APP_PORT+1;
		else if ((sts_work_mode == STS_RSS_MODE)||(sts_work_mode == STS_REEDSWITCH_MODE)||(sts_work_mode == STS_NETWORK_MODE)||(sts_work_mode == STS_WIRED_MODE))
			AppData.Port = (uint8_t)YUNHORN_STS_O2_LORA_APP_HTBT_PORT; //LORAWAN_USER_APP_PORT+1;

		//AppData.Port = (uint8_t)sts_sendhtbtport; //LORAWAN_USER_APP_PORT+1;

		AppData.Buffer[i++]		= (uint8_t)(AppLedStateOn|0x80)&0x0ff;
		AppData.Buffer[i++] 	= (uint8_t)(99*batteryLevel/254)&0xff;					//#05

	 } else if ((sensor_data_ready!= 0U)) //sensor_data_ready for manual push button-1 trigger)
	    {
			sensor_data_ready =0;
			if (sts_work_mode > STS_RSS_MODE)  // keep compatible with previous version, PIXEL-NETWORK
			{
				AppData.Buffer[i++] = (uint8_t)(AppLedStateOn|0x80)&0x0ff;									//00 ************  MUST KEEP IT HERE, NON-ZERO   ******
			}

			AppData.Buffer[i++] = (uint8_t)(sensorData.lamp_bar_color)&0xff;  			//01
			AppData.Buffer[i++] = (uint8_t)(sensorData.workmode)&0xff;					//02 WORK MODE
			if (sts_work_mode == STS_REEDSWITCH_MODE)
			{
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor1_on_off)&0xff;		//03 Sensor head #1 status reed switch
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor2_on_off)&0xff;		//04 Sensor head #2 status reed switch
			} else if (sts_work_mode == STS_RSS_MODE)
			{
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor1_on_off)&0xff;		//03 Sensor head #1 status reed switch
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor3_on_off)&0xff;		//04 Sensor head #3 status RSS motion
			}
			else if (sts_work_mode == STS_DUAL_MODE)
			{
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor1_on_off)&0xff;		//03 Sensor head #1 status reed switch
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor2_on_off)&0xff;		//04 Sensor head #2 status SOS button
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor3_on_off)&0xff;		//05 Sensor head #3 status RSS motion

				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_state)&0xff;			//06 occupancy over time or not
				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_duration>>8)&0xff;		//07 occupancy over stay duration MSB
				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_duration)&0xff;		//08 occupancy over stay duration LSB

			} else if (sts_work_mode == STS_UNI_MODE)
			{
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor1_on_off)&0xff;		//03 Sensor head #1 status reedswitch
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor2_on_off)&0xff;		//04 Sensor head #2 status SOS button
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor3_on_off)&0xff;		//05 Sensor head #3 status RSS motion
				AppData.Buffer[i++] = (uint8_t)(sensorData.state_sensor4_on_off)&0xff;		//06 Sensor head #4 status reserved


				AppData.Buffer[i++] = (uint8_t)(sensorData.rss_presence_distance>>8)&0xff;	//07 MSB distance
				AppData.Buffer[i++] = (uint8_t)(sensorData.rss_presence_distance)&0xff;		//08 LSB distance
				AppData.Buffer[i++]	= (uint8_t)(sensorData.rss_presence_score>>8)&0xff;		//09 MSB score
				AppData.Buffer[i++]	= (uint8_t)(sensorData.rss_presence_score)&0xff;		//10 LSB score

				AppData.Buffer[i++]	= (uint8_t)(sensorData.unconscious_state)&0xff;			//11 unconscious state detected or not
				AppData.Buffer[i++]	= (uint8_t)(sensorData.fall_state)&0xff;				//12 fall detected or not
				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_state)&0xff;			//13 occupancy over time or not
				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_duration>>8)&0xff;		//14 occupancy over stay duration MSB
				AppData.Buffer[i++]	= (uint8_t)(sensorData.over_stay_duration)&0xff;		//15 occupancy over stay duration LSB
				AppData.Buffer[i++]	= (uint8_t)(sensorData.no_movement_duration>>8)&0xff;	//16 occupancy over stay duration LSB
				AppData.Buffer[i++]	= (uint8_t)(sensorData.no_movement_duration)&0xff;		//17 occupancy over stay duration LSB

				AppData.Buffer[i++]	= (uint8_t)(sensorData.fall_speed)&0xff;				//18 fall detected speed
				AppData.Buffer[i++]	= (uint8_t)(sensorData.fall_gravity)&0xff;				//19 fall detected gravity

				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_start_timestamp>>24)&0xff;				//20 SOS start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_start_timestamp>>16)&0xff;				//21 SOS start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_start_timestamp>>8)&0xff;				//22 SOS start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_start_timestamp)&0xff;					//23 SOS start time stamp

				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_stop_timestamp>>24)&0xff;				//24 SOS stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_stop_timestamp>>16)&0xff;				//25 SOS stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_stop_timestamp>>8)&0xff;				//26 SOS stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor2_stop_timestamp)&0xff;					//27 SOS stop time stamp


				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_start_time_stamp>>24)&0xff;				//28 fall start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_start_time_stamp>>16)&0xff;				//29 fall start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_start_time_stamp>>8)&0xff;				//30 fall start time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_start_time_stamp)&0xff;					//31 fall start time stamp

				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_stop_time_stamp>>24)&0xff;				//32 fall stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_stop_time_stamp>>16)&0xff;				//33 fall stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_stop_time_stamp>>8)&0xff;					//34 fall stop time stamp
				AppData.Buffer[i++]	= (uint8_t)(sensorData.event_sensor3_fall_stop_time_stamp)&0xff;					//35 fall stop time stamp

			}
			uint8_t ich= (sts_lamp_bar_color>>4 & 0x0f);
			uint8_t icl= (sts_lamp_bar_color & 0x0f);
			char colorshow[30]="";
			strcpy(colorshow, (ich==0)?"":sts_lamp_color_code[ich]);

			APP_LOG(TS_OFF, VLEVEL_L,
				"\r\n######| Color = %s%s  | Mode = %5s |\r\n",(char *)colorshow, sts_lamp_color_code[icl], (char*)sts_work_mode_code[sensorData.workmode]);
			if (sts_work_mode == STS_UNI_MODE)
			{
				APP_LOG(TS_OFF, VLEVEL_L,
						"\r\n######| S1-Door     | S2-SOS   | S3-Motion   |  S4    |Distance(mm) | MotionScore| Unconscious | Over_Stay_(min)   | Fall Detected|"
						"\r\n######|    %s    | %s  |    %d    |    %1d   |   %04d      |   %04d  |    %1d       |     %4d    | %s  |\r\n",
						sts_door_status_code[sensorData.state_sensor1_on_off],
						sts_sos_status_code[sensorData.state_sensor2_on_off],
						sensorData.state_sensor3_on_off,
						sensorData.state_sensor4_on_off,
						(uint16_t)sensorData.rss_presence_distance,(uint16_t)sensorData.rss_presence_score,
						sensorData.unconscious_state, sensorData.over_stay_duration, sts_fall_mode_code[sensorData.fall_state]);
			} else if (sts_work_mode == STS_DUAL_MODE) {

				APP_LOG(TS_OFF, VLEVEL_L,
									"\r\n######| S1-Door     | S2-SOS   | S3-Motion   |\r\n"
									"\r\n######|    %s    |      %s    |    %d        |\r\n",
									sts_door_status_code[sensorData.state_sensor1_on_off],
									sts_sos_status_code[sensorData.state_sensor2_on_off],
									sensorData.state_sensor3_on_off);
			} else if (sts_work_mode == STS_RSS_MODE) {
				APP_LOG(TS_OFF, VLEVEL_L,
									"\r\n######| S3-Motion   |\r\n"
									"\r\n######|    %d        |\r\n",sensorData.state_sensor3_on_off);
			}
	}

	AppData.BufferSize = (uint8_t)(sts_service_mask > STS_SERVICE_MASK_L1? 0:i)&0xff;;
#ifdef 	CLOCK_SYNC
    if( IsClockSynched == false )
    {
        status = LmhpClockSyncAppTimeReq( );

        if (LORAMAC_HANDLER_SUCCESS == status) {
        	OnSysTimeUpdate();
        }
    }
#endif
	if ((JoinLedTimer.IsRunning) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET))
	{
		UTIL_TIMER_Stop(&JoinLedTimer);
#ifndef	STM32WLE5xx
		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_M, "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);
	}

  /* USER CODE END SendTxData_1 */
}

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

  /* USER CODE END OnTxTimerEvent_1 */

  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)
{
#ifndef	STM32WLE5xx
  HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET); /* LED_GREEN */
#endif
}

static void OnRxTimerLedEvent(void *context)
{
#ifndef	STM32WLE5xx
  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); /* LED_BLUE */
#endif
}

static void OnJoinTimerLedEvent(void *context)
{
#ifndef	STM32WLE5xx
  HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin); /* LED_RED */
#endif
  //HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); /* STS GREEN */
  if ((sts_work_mode != STS_WIRED_MODE))
  UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP3), CFG_SEQ_Prio_0);
}

/* USER CODE END PrFD_LedEvents */

static void OnYunhornSTSLampBarColorTimerEvent(void *context)
{

  //uint8_t lum=DEFAULT_LUMINANCE_LEVEL;
  //HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin); /* STS GREEN */
  //if ((sts_work_mode != STS_WIRED_MODE))
	uint8_t high4=(sts_lamp_bar_color>>4)&0x0f, low4=sts_lamp_bar_color&0x0f;
#if 0
	luminance_level += 10;
	luminance_level %= 60;
	luminance_level = MAX(10,luminance_level);
#endif
	//if (STSLampBarColorTimer.IsRunning)
	{
		UTIL_TIMER_Stop(&STSLampBarColorTimer);

		if (high4==0)
		{
			  //if (last_sts_lamp_bar_color != sts_lamp_bar_color)
			  {
				  //__disable_irq();
				  STS_Lamp_Bar_Set_STS_RGB_Color(sts_lamp_bar_color, luminance_level);
				  //__enable_irq();
				  //STS_WS2812B_Refresh();
				  last_sts_lamp_bar_color = sts_lamp_bar_color;
			  }

		} else {
			  if (r_b)
			  {
				  //__disable_irq();
				  STS_Lamp_Bar_Set_STS_RGB_Color(high4, luminance_level);
				  //__enable_irq();
				  //last_sts_lamp_bar_color = high4;
				  //STS_WS2812B_Refresh();
			  }
			  else {
				  //__disable_irq();
				  STS_Lamp_Bar_Set_STS_RGB_Color(low4, luminance_level);
				  //__enable_irq();
				  //last_sts_lamp_bar_color = low4;
				  //STS_WS2812B_Refresh();
			  }

			  r_b = !r_b;
		}

		UTIL_TIMER_Start(&STSLampBarColorTimer);
	}

}

static void OnYunhornSTSDurationCheckTimerEvent(void *context)
{
	static bool over_threshold = FALSE;
	SysTime_t current_time = SysTimeGetMcuTime();


	if (STS_Status_Door_Close==sts_hall1_read)
	{
		sts_o7_sensorData.event_sensor1_duration = current_time.Seconds - sts_o7_sensorData.event_sensor1_start_time;

		//if (sts_o7_sensorData.event_sensor1_duration > sts_occupancy_overtime_threshold_in_10min*600)
		if (sts_o7_sensorData.event_sensor1_duration > (sts_occupancy_overtime_threshold_in_10min*60)) //for debug
		{
			sts_o7_sensorData.over_stay_state = 1;
			sts_o7_sensorData.over_stay_duration = sts_o7_sensorData.event_sensor1_duration;
			over_threshold = TRUE;
			APP_LOG(TS_OFF, VLEVEL_L, "\r\nSensor 1 Over Stay State=%d,   Duration= %d Sec,  Threshold =%u \r\n",
					sts_o7_sensorData.over_stay_state,
					(sts_o7_sensorData.over_stay_duration),
					(sts_occupancy_overtime_threshold_in_10min*60) );
			sts_lamp_bar_color = STS_RED_DARK;
			//sts_lamp_bar_flashing_color = 0x20;
			//volatile uint8_t sts_lamp_bar_color = STS_GREEN;		//puColor
			//volatile uint8_t sts_lamp_bar_flashing_color = 0x23; // RED_BLUE;
		}

	} else {
		sts_o7_sensorData.event_sensor1_duration =0;

		sts_o7_sensorData.over_stay_state = 0;
	}

	// to be defined later for SOS threshold TODO XXXX
	if (STS_Status_SOS_Pushdown==sts_hall2_read)
	{
		sts_o7_sensorData.event_sensor2_duration = current_time.Seconds - sts_o7_sensorData.event_sensor2_start_time;
		if (sts_o7_sensorData.event_sensor2_duration > sts_occupancy_overtime_threshold_in_10min*60)  //for debug
		{
				sts_o7_sensorData.over_stay_state = 1;
				// 2024-07-15 update, no overwrite sensor1 duration value
				//sts_o7_sensorData.over_stay_duration = sts_o7_sensorData.event_sensor2_duration;
				//
				over_threshold = TRUE;
		}
			// to be defiend later for SOS threshold TODO XXXX

	} else {
		sts_o7_sensorData.event_sensor2_duration =0;
		//sts_o7_sensorData.over_stay_state = 0;

	}

	if (sts_rss_result==STS_RESULT_MOTION)
	{
		sts_o7_sensorData.event_sensor3_motion_duration = current_time.Seconds - sts_o7_sensorData.event_sensor3_motion_start_time;
		//if (sts_o7_sensorData.event_sensor3_motion_duration > sts_occupancy_overtime_threshold_in_10min*600)
		if (sts_o7_sensorData.event_sensor3_motion_duration > sts_occupancy_overtime_threshold_in_10min*60) //for debug
		{
			sts_o7_sensorData.occupancy_over_stay_state = 1;
			sts_o7_sensorData.occupancy_duration =sts_o7_sensorData.event_sensor3_motion_duration;
			over_threshold = TRUE;
		}
	} else {
		sts_o7_sensorData.event_sensor3_motion_duration =0;
		//sts_o7_sensorData.occupancy_over_stay_state = 0;
	}

	/*
	STS_PRESENCE_NONE=0,
	STS_PRESENCE_FALL,
	STS_PRESENCE_RISING,
	STS_PRESENCE_LAYDOWN,
	STS_PRESENCE_UNCONSCIOUS,
	STS_PRESENCE_STAYSTILL
	*/
	uint32_t time_stamp=STS_Get_Date_Time_Stamp(); //uint8_t datetimestamp[8]={0};
	//(&time_stamp, datetimestamp);

	switch (sts_fall_rising_detected_result){
		case STS_PRESENCE_NORMAL:
			sts_o7_sensorData.fall_state = STS_PRESENCE_NORMAL;
			sts_o7_sensorData.fall_laydown_duration=0;
			sts_o7_sensorData.unconscious_state =STS_PRESENCE_NORMAL;
			sts_o7_sensorData.unconscious_duration =0;
			sts_o7_sensorData.no_movement_duration =0;
		break;
		case STS_PRESENCE_FALL:
			// sts_fall_confirm_threshold_in_10sec
			sts_o7_sensorData.event_sensor3_fall_duration = current_time.Seconds - sts_o7_sensorData.event_sensor3_fall_start_time;
			if (sts_o7_sensorData.event_sensor3_fall_duration > 10*sts_cfg_nvm.fall_confirm_threshold_in_10sec)
			{
					//sts_o7_sensorData.occupancy_over_stay_state = 1;
					//sts_o7_sensorData.event_sensor3_fall_start_time_stamp = time_stamp;
					sts_o7_sensorData.fall_state = sts_fall_rising_detected_result;
					sts_o7_sensorData.fall_laydown_duration =sts_o7_sensorData.event_sensor3_fall_duration;
					over_threshold = TRUE;
			} else {   // still laydown, but not over fall down confirmation threshold
				sts_o7_sensorData.fall_state = STS_PRESENCE_LAYDOWN;
				sts_o7_sensorData.fall_laydown_duration =sts_o7_sensorData.event_sensor3_fall_duration;
				over_threshold = FALSE;
			}

		break;

		case STS_PRESENCE_RISING:
			sts_o7_sensorData.event_sensor3_fall_stop_time_stamp = time_stamp;
			over_threshold = FALSE;
		break;

		case STS_PRESENCE_LAYDOWN:

		break;

		case STS_PRESENCE_UNCONSCIOUS:
		case STS_PRESENCE_STAYSTILL:
		case STS_PRESENCE_NO_MOVEMENT:
			sts_o7_sensorData.fall_state = sts_fall_rising_detected_result;
			 sts_o7_sensorData.event_sensor3_no_movement_duration = current_time.Seconds - sts_o7_sensorData.event_sensor3_no_movement_start_time;
			 sts_o7_sensorData.event_sensor3_unconcious_duration = current_time.Seconds - sts_o7_sensorData.event_sensor3_unconcious_start_time;
			 // sts_motionless_duration_threshold_in_min
			 if ((sts_o7_sensorData.event_sensor3_no_movement_duration > sts_cfg_nvm.motionless_duration_threshold_in_min*60)||
					 (sts_o7_sensorData.unconscious_duration > sts_cfg_nvm.motionless_duration_threshold_in_min*60))
			 {
				 sts_o7_sensorData.unconscious_state = 1;
				 over_threshold = TRUE;
			 }
		break;
	}


#if 0
	if (sts_xxx_result == xxx)
			sts_o7_sensorData.event_sensor3_event_duration = sensor_event_time.Seconds - sts_o7_sensorData.event_sensor4_start_time;
#endif

	if (over_threshold == TRUE)
	{

		if (sts_o7_sensorData.fall_state == STS_PRESENCE_LAYDOWN)
		{
			sts_status_color = sts_lamp_bar_flashing_color;   //STS_RED_BLUE;
			sts_lamp_bar_color = sts_lamp_bar_flashing_color; //STS_RED_BLUE;
		} else {
			sts_status_color = STS_RED_DARK;
			sts_lamp_bar_color = STS_RED_DARK; //sts_lamp_bar_flashing_color;
		}

		sensor_data_ready = 1;
		UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
		over_threshold = FALSE;
	}
}


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)
    {
#ifndef	STM32WLE5xx
      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);

      sts_lamp_bar_color = STS_GREEN;

#ifndef	STM32WLE5xx
      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;
      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_H, "###### U/L FRAME:JOIN | DR:%d | PWR:%d\r\n", joinParams->Datarate, joinParams->TxPower);
  }

  if (STS_LoRa_WAN_Joined)
  {
	  heart_beat_timer = 1;
	  SendTxData();

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

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

	  //UTIL_TIMER_Start(&TxTimer);
	  //UTIL_TIMER_Start(&STSDurationCheckTimer);
	  OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);

	  UTIL_TIMER_Create(&YunhornSTSRSSWakeUpTimer, YUNHORN_STS_RSS_WAKEUP_CHECK_TIME, UTIL_TIMER_PERIODIC, OnYunhornSTSOORSSWakeUpTimerEvent, NULL);
	  UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);

		STS_SENSOR_Distance_Test_Process();

		APP_LOG(TS_OFF, VLEVEL_H, "\r\nRSS Measured Distance=[%u] mm \r\n", (uint16_t)sts_distance_rss_distance);

  }

  /* 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 */
	 IsClockSynched = true;
  /* USER CODE END OnSysTimeUpdate_1 */
}

static void OnClassChange(DeviceClass_t deviceClass)
{
  /* USER CODE BEGIN OnClassChange_1 */
  APP_LOG(TS_OFF, VLEVEL_H, "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,"**************** TxPeriodicity = %u (sec)\r\n", (TxPeriodicity/1000) );
  /* 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 */
#ifndef	STM32WLE5xx
  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 */
#ifndef	STM32WLE5xx
  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)
{
	if ((sts_work_mode == STS_RSS_MODE)||(sts_work_mode == STS_DUAL_MODE)||(sts_work_mode == STS_UNI_MODE))
	{
		UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_YunhornSTSEventP2), CFG_SEQ_Prio_0);
		APP_LOG(TS_OFF,VLEVEL_M,"\r\n Wakeup Timer ======== RSS result changed flag=%d \r\n", sts_rss_result_changed_flag);
		if ((STS_LoRa_WAN_Joined != 0)&&(sts_rss_result_changed_flag==1))
		{
			sts_rss_result_changed_flag = 0;
			APP_LOG(TS_OFF,VLEVEL_M,"\r\n Wakeup Timer ======== Send Tx Result\r\n");
			SendTxData();
			//UTIL_SEQ_SetTask((1 << CFG_SEQ_Task_LoRaSendOnTxTimerOrButtonEvent), CFG_SEQ_Prio_0);
			//if ((last_sts_rss_result ==STS_RESULT_NO_MOTION)&& (sts_rss_result==STS_RESULT_MOTION))
				{
					//OnSensor3StateChanged();
				}
		}

	}

	//UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);

}

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

	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);
	}
	UTIL_TIMER_Start(&YunhornSTSHeartBeatTimer);
}

/**
  * @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 = %u (sec)\r\n", HeartBeatPeriodicity/1000 );
  /* USER CODE END OnTxPeriodicityChanged_2 */
}

/**
  * @brief  Yunhorn STS  Sensor Sampling Periodicity/interval Change callback function
  * @param  context ptr of STS RSS WakeUp context
  */

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

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

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

  /* Update timer periodicity */

#if	defined(STS_O6)||defined(STS_O7)
  if (STS_LoRa_WAN_Joined !=0)
  {
	  UTIL_TIMER_Stop(&YunhornSTSRSSWakeUpTimer);
	  UTIL_TIMER_SetPeriod(&YunhornSTSRSSWakeUpTimer, SamplingPeriodicity);
	  UTIL_TIMER_Start(&YunhornSTSRSSWakeUpTimer);
	  APP_LOG(TS_OFF, VLEVEL_M,"**************** Sampling Timer Periodicity = %u (sec)\r\n", (SamplingPeriodicity/1000) );
  }

#else
  UTIL_TIMER_Stop(&YunhornSTSSamplingCheckTimer);
  UTIL_TIMER_SetPeriod(&YunhornSTSSamplingCheckTimer, SamplingPeriodicity);
  UTIL_TIMER_Start(&YunhornSTSSamplingCheckTimer);
  APP_LOG(TS_OFF, VLEVEL_M,"**************** SamplingPeriodicity = %u (sec)\r\n", (SamplingPeriodicity/1000) );
#endif

  /* USER CODE BEGIN OnTxPeriodicityChanged_2 */

  /* USER CODE END OnTxPeriodicityChanged_2 */
}

/**
  * @brief  Yunhorn STS Sensor Sampling Check Timer callback function
  * @param  context ptr of STS Sampling Check context
  */
/*
static void OnYunhornSTSSamplingCheckTimerEvent(void *context)
{
   	// P1    --- REEDSWITCH, HALL ELEMENT, WATER LEAKAGE, ********** NOT FOR THIS EVENT TRIGGER PROCESS
   	// 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           ****** NOT FOR THIS REAL-TIME FULL BLOCKED DETECTION
   	// P6    ---
   	// P7    ---
   	// P8    --- AIR QUALITY AND ODOR LEVEL, SMOKING DETECTION

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


/*
 * 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
 *
 */

void USER_APP_AUTO_RESPONDER_Parse(char *tlv_buf, uint8_t tlv_buf_size)
{
	uint8_t i=0, mems_ver, invalid_flag=1;
	UTIL_MEM_set_8((void*)outbuf,0x0, sizeof(outbuf));

	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);
					__set_FAULTMASK(1);
					OnSystemReset();

				}	else if 	((char)tlv_buf[CFG_CMD3] == 'S') {		// Self Function Testing "YZS"
					i=0;
					//outbuf[i++] = (uint8_t) 'Y';
					//outbuf[i++] = (uint8_t) 'Z';
					//outbuf[i++] = (uint8_t) 'S';
					//STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);

					i=0;

					STS_SENSOR_Function_Test_Process();
#if 0
					HAL_Delay(5000);
					i=21;
					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
#endif
				} 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, (uint8_t *)outbuf);

					STS_SENSOR_Distance_Test_Process();

					APP_LOG(TS_OFF, VLEVEL_H, "\r\nRSS Measured Distance=[%u] mm \r\n", (uint16_t)sts_distance_rss_distance);

					// Store valid installation height value
					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.work_mode = (uint8_t)sts_work_mode;
					sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;
					sts_cfg_nvm.sensor_install_height_in_10cm 	= sts_sensor_install_height/100;  //in 10 cm
					OnStoreSTSCFGContextRequest();


					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));

#if	defined(STS_O7)||defined(STS_O6)
					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"
				{
					i=0;
					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();
					if (sts_service_mask != STS_SERVICE_MASK_L0) STS_Lamp_Bar_Set_Dark();
					//STS_Combined_Status_Processing();
				}
			break;

			case 'V':	//"YV"
				if ((char)tlv_buf[CFG_CMD3] == 'H') { // "YVH"   REPORT FIRMWARE VERSION "YVH"
					// FIRMWARE VERSION REPORT
					i=0;
					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) sts_hardware_ver;
					outbuf[i++] = (uint8_t) MajorVer;
					outbuf[i++] = (uint8_t) MinorVer;
					outbuf[i++] = (uint8_t) SubMinorVer;

					 struct tm localtime;
					 SysTime_t UnixEpoch = SysTimeGet();
					 UnixEpoch.Seconds -= 18; /*removing leap seconds*/

					 //UnixEpoch.Seconds += 3600 * 2; /*adding 2 hours*/

					 SysTimeLocalTime(UnixEpoch.Seconds,  &localtime);

					  APP_LOG(TS_OFF, VLEVEL_M, "LTIME:%02dh%02dm%02ds on %02d/%02d/%04d\r\n",
					            localtime.tm_hour, localtime.tm_min, localtime.tm_sec,
					            localtime.tm_mday, localtime.tm_mon + 1, localtime.tm_year + 1900);
#if 0
					LmhPackage_t LmhpClockSyncPackageFactory;
					LmhpClockSyncPackageFactory.Init;

					if (LmhpClockSyncPackageFactory.IsInitialized)
						LmhpClockSyncPackageFactory.Process;

					if (LORAMAC_HANDLER_SUCCESS == LmhpClockSyncAppTimeReq()) {
						APP_LOG(TS_OFF, VLEVEL_M, "\r\n Clock Sync Success \r\n");
					}
					SysTime_t	mytime=SysTimeGet();
					struct tm mylocal;
					SysTimeLocalTime((uint32_t)mytime.Seconds, &mylocal);

					APP_LOG(TS_OFF, VLEVEL_M, "\r\nTime YYYYMMDD=%4d:%2d:%2d HHMMSS=%2d:%2d:%2d \r\n",
							mylocal.tm_year, mylocal.tm_mon, mylocal.tm_mday, mylocal.tm_hour, mylocal.tm_min, mylocal.tm_sec);
#endif
					i=0;
					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) sts_hardware_ver;
					outbuf[i++] = (uint8_t) MajorVer;
					outbuf[i++] = (uint8_t) MinorVer;
					outbuf[i++] = (uint8_t) SubMinorVer;
					uint16_t year = localtime.tm_year+1900;
					outbuf[i++] = (uint8_t) ((year)>>8);
					outbuf[i++] = (uint8_t) (year)&0xff;

					outbuf[i++] = (uint8_t) localtime.tm_mon+1;
					outbuf[i++] = (uint8_t) localtime.tm_mday;
					outbuf[i++] = (uint8_t) localtime.tm_hour;
					outbuf[i++] = (uint8_t) localtime.tm_min;
					outbuf[i++] = (uint8_t) localtime.tm_sec;

					STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
					APP_LOG(TS_OFF, VLEVEL_M, "###### YUNHORN Report Version [ %10x ] \r\n", (uint8_t *)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;
					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];  			//sts ioc 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));
							PME_ON;

							// 2024-07-31
							UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
							UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf,tlv_buf_size);
							i = tlv_buf_size;
							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));
						PME_OFF;
						// 2024-07-31
						UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf,tlv_buf_size);
						i = tlv_buf_size;
						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) {
						PME_ON;
						PME_OFF;
						PME_ON;
						// 2024-07-31
						UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf,tlv_buf_size);
						i = tlv_buf_size;
						STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t *)outbuf);
					} else {
						STS_SENSOR_Upload_Config_Invalid_Message();
					}
			break;

			case 'D':			//periodicity UPLINK, ACT AS HEART-BEAT MESSAGE
				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;
						}
						periodicity_length = periodicity_length*1000;	//translate to 1000ms=1s


						//OnTxPeriodicityChanged(TxPeriodicity);
#if	defined(STS_O6)||defined(STS_O7)
						HeartBeatPeriodicity = periodicity_length;
						OnYunhornSTSHeartBeatPeriodicityChanged(periodicity_length);
#endif
						// 2024-07-31
						UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf,tlv_buf_size);
						i = tlv_buf_size;

						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_M, "###### 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
#if	defined(STS_O6)||defined(STS_O7)
					SamplingPeriodicity = heart_beat_or_sampling_periodicity_length*1000;  //translate to 1000ms=1s
					OnYunhornSTSSamplingPeriodicityChanged(SamplingPeriodicity);
#endif
						// 2024-07-31
					    UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf,tlv_buf_size);
						i = tlv_buf_size;

						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_M, "###### YUNHORN HeartBeat or Sampling Interval Changed to [ %d ] msec\r\n", (SamplingPeriodicity));

					} 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 == 5 && tlv_buf[CFG_CMD4]=='0')  //switch network mode color
					{
						invalid_flag = 0;

						sts_work_mode = (uint8_t)(tlv_buf[CFG_CMD4] - 0x30);
						if (sts_work_mode == STS_NETWORK_MODE)
						{	//network mode
							sts_cloud_netcolor = (uint8_t)(tlv_buf[CFG_CMD5]-0x30);
							if (sts_cloud_netcolor == STS_COLOR_MAX) sts_cloud_netcolor = STS_RED_BLUE;
							else if (sts_cloud_netcolor > STS_COLOR_MAX)
							{
#if 1
								switch((uint8_t)tlv_buf[CFG_CMD5])
								{
									case STS_GREENDARK:
										sts_lamp_bar_color = STS_GREEN_DARK;
										sts_cloud_netcolor = STS_GREEN_DARK;
									break;
									case STS_REDDARK:
										sts_lamp_bar_color = STS_RED_DARK;
										sts_cloud_netcolor = STS_RED_DARK;
									break;
									case STS_REDBLUE:
										sts_lamp_bar_color = STS_RED_BLUE;
										sts_cloud_netcolor = STS_RED_BLUE;
									break;
									case STS_BLUEDARK:
										sts_lamp_bar_color = STS_BLUE_DARK;
										sts_cloud_netcolor = STS_BLUE_DARK;
									break;
									case STS_REDGREEN:
										sts_lamp_bar_color = STS_RED_GREEN;
										sts_cloud_netcolor = STS_RED_GREEN;
									break;
									case STS_BLUEGREEN:
										sts_lamp_bar_color = STS_BLUE_GREEN;
										sts_cloud_netcolor = STS_BLUE_GREEN;
									break;
								}
							}
#endif
							APP_LOG(TS_OFF, VLEVEL_L, "\r\n Cloud Color Set to 0X%02X \r\n",sts_cloud_netcolor);
						}
						sts_service_mask = STS_SERVICE_MASK_L0;

						sts_cfg_nvm.work_mode = (uint8_t)sts_work_mode;
						sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;
						OnStoreSTSCFGContextRequest();

						i=0;						// Step 1: Prepare status update message
						UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
						i = tlv_buf_size;

						STS_Combined_Status_Processing();

					} else if  (tlv_buf_size == 7 && tlv_buf[CFG_CMD4]=='F')  // Change fall detection
					{
						invalid_flag = 0;									// P 1 1 F A B C
						if (((tlv_buf[CFG_CMD5] >='0') && (tlv_buf[CFG_CMD5]<='9')) && ((tlv_buf[CFG_CMD6]<='9') && (tlv_buf[CFG_CMD6]>='0'))
								&& ((tlv_buf[CFG_CMD7]<='9') && (tlv_buf[CFG_CMD7]>='0'))) //&& ((tlv_buf[CFG_CMD8]<='9') && (tlv_buf[CFG_CMD8]>='0')))
						{
							sts_fall_detection_acc_threshold 			= (uint8_t)(tlv_buf[CFG_CMD5] - 0x30)*10;			//A: acc *10 mg/s2
							sts_fall_detection_depth_threshold 			= (uint8_t)(tlv_buf[CFG_CMD6] - 0x30)*10;			//D: depth *10 in cm
							sts_fall_confirm_threshold_in_10sec			= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30)*10;			//C: fall_confirm_threshold_in_10sec

							//sts_occupancy_overtime_threshold_in_10min 	= (uint8_t)(tlv_buf[CFG_CMD8] - 0x30)*10;			//T: overtime *10 min

							sts_cfg_nvm.fall_detection_acc_threshold 	= (uint8_t)(tlv_buf[CFG_CMD5] - 0x30);
							sts_cfg_nvm.fall_detection_depth_threshold 	= (uint8_t)(tlv_buf[CFG_CMD6] - 0x30);
							sts_cfg_nvm.fall_confirm_threshold_in_10sec	= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30);

							//sts_cfg_nvm.occupancy_overtime_threshold_in_10min 	= (uint8_t)(tlv_buf[CFG_CMD8] - 0x30);

							if (sts_work_mode == STS_UNI_MODE)		// fall detection threshold only effective in Uni_mode
							{
								if ((sts_fall_detection_acc_threshold ==0)&&(sts_fall_detection_depth_threshold==0))
								{
									sts_presence_fall_detection = FALSE;
								} else {
									sts_presence_fall_detection = TRUE;
								}
							}

							OnStoreSTSCFGContextRequest();

							i=0;						// Step 1: Prepare status update message
							UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
							UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
							i = tlv_buf_size;
							APP_LOG(TS_OFF, VLEVEL_L, "###### Fall detection CFG = %s\r\n",(char*)outbuf);

							STS_Combined_Status_Processing();
						}



					}else if  (tlv_buf_size == 8 && tlv_buf[CFG_CMD4]=='O')  // Change occupancy/motionless/ unconscious  overtime threshold
					{
						invalid_flag = 0;									// P 1 1 O A B C D
						if (((tlv_buf[CFG_CMD5] >='0') && (tlv_buf[CFG_CMD5]<='9')) && ((tlv_buf[CFG_CMD6]<='9') && (tlv_buf[CFG_CMD6]>='0'))
								&& ((tlv_buf[CFG_CMD7]<='9') && (tlv_buf[CFG_CMD7]>='0'))&& ((tlv_buf[CFG_CMD8]<='9') && (tlv_buf[CFG_CMD8]>='0')))
						{
							sts_motionless_duration_threshold_in_min	= (uint8_t)(tlv_buf[CFG_CMD5] - 0x30);			//Motionless duration in min
							sts_occupancy_overtime_threshold_in_10min	= (uint8_t)(tlv_buf[CFG_CMD6] - 0x30)*10;			//Long occupation in min
							sts_unconscious_level_threshold				= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30+1)*128;		//motion level threshold less than 1280
							sts_alarm_mute_reset_timer_in_10sec		 	= (uint8_t)(tlv_buf[CFG_CMD8] - 0x30)*10;			//alarm mute reset timer in 10 sec

							sts_cfg_nvm.motionless_duration_threshold_in_min 	= (uint8_t)(tlv_buf[CFG_CMD5] - 0x30);
							sts_cfg_nvm.occupancy_overtime_threshold_in_10min 	= (uint8_t)(tlv_buf[CFG_CMD6] - 0x30);
							sts_cfg_nvm.unconscious_or_motionless_level_threshold		= (uint8_t)(tlv_buf[CFG_CMD7] - 0x30+1);
							sts_cfg_nvm.alarm_mute_reset_timer_in_10sec 	= (uint8_t)(tlv_buf[CFG_CMD8] - 0x30)*10;

							OnStoreSTSCFGContextRequest();

							i=0;						// Step 1: Prepare status update message
							UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
							UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
							i = tlv_buf_size;
							APP_LOG(TS_OFF, VLEVEL_L, "###### Occupancy/Overstay/unconscious config changed =%s\r\n",(char *)outbuf);
							STS_Combined_Status_Processing();
						}
					}
					else if  (tlv_buf_size == 4 )		// P WORK mode switch
					{
						invalid_flag = 0;

						if (((tlv_buf[CFG_CMD4] >='0') && (tlv_buf[CFG_CMD4]<='9')) || ((tlv_buf[CFG_CMD4]<='Z') && (tlv_buf[CFG_CMD4]>='A')))
						{
							if ((tlv_buf[CFG_CMD4] >='0') && (tlv_buf[CFG_CMD4]<='9'))
							{
								sts_work_mode = (uint8_t)((tlv_buf[CFG_CMD4] - 0x30));
							} else if ((tlv_buf[CFG_CMD4]<='Z') && (tlv_buf[CFG_CMD4]>='A'))
							{
								sts_work_mode = (uint8_t)((tlv_buf[CFG_CMD4] - 0x41) + 10);
							}

						i=0;						// Step 1: Prepare status update message
						UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
						UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
						i = tlv_buf_size;
						APP_LOG(TS_OFF, VLEVEL_L, "###### P Work mode switched =%s\r\n",(char *)outbuf);

						//sts_service_mask = STS_SERVICE_MASK_L0;
						//sts_lamp_bar_color = STS_GREEN;
						sts_presence_fall_detection=FALSE;
						sts_rss_config_updated_flag = STS_RSS_CONFIG_DEFAULT;

						switch (sts_work_mode) {
							case STS_NETWORK_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_0_MASK;
							break;

							case STS_WIRED_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_1_MASK;
							break;

							case STS_REEDSWITCH_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_2_MASK;
							break;

							case STS_RSS_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_3_MASK;
							sts_rss_config_updated_flag = STS_RSS_CONFIG_DEFAULT;
							break;

							case STS_DUAL_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_4_MASK;
							sts_rss_config_updated_flag = STS_RSS_CONFIG_DEFAULT;
							break;

							case STS_UNI_MODE:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MODE_5_MASK;
							sts_rss_config_updated_flag = STS_RSS_CONFIG_FALL_DETECTION;
							sts_presence_fall_detection=TRUE;
							break;

							default:
							sts_cfg_nvm.sts_ioc_mask = STS_IOC_MASK_NONE;
							break;
						}
						sts_cfg_nvm.work_mode = (uint8_t)sts_work_mode;

						sts_cfg_nvm.sts_service_mask = (uint8_t)sts_service_mask;
						OnStoreSTSCFGContextRequest();

						STS_Combined_Status_Processing();
						HAL_Delay(2000);

						//OnSystemReset();

						} else {
							invalid_flag = 1;
						}

					} else if (tlv_buf_size == CFG_CMD_RSS_FULL_SIZE)
					{
							invalid_flag = 0;

							for (j =0; j < CFG_CMD_RSS_FULL_SIZE-3; j++)
							{
								if ((tlv_buf[CFG_CMD4+j] >='0') && (tlv_buf[CFG_CMD4+j]<='9'))
								{
									//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]);
								} else {
									invalid_flag = 1;
								}
							}
							i=0;
							sts_cfg_nvm.p[RSS_CFG_START_M] 					= (uint8_t)((tlv_buf[CFG_CMD4] - 0x30)*10+(tlv_buf[CFG_CMD5] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_LENGTH_M] 				= (uint8_t)((tlv_buf[CFG_CMD6] - 0x30)*10+(tlv_buf[CFG_CMD7] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_THRESHOLD] 				= (uint8_t)((tlv_buf[CFG_CMD8] - 0x30)*10+(tlv_buf[CFG_CMD9] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_RECEIVER_GAIN] 			= (uint8_t)((tlv_buf[CFG_CMD10]- 0x30)*10+(tlv_buf[CFG_CMD11]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_PROFILE] 					= (uint8_t)((tlv_buf[CFG_CMD12]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_RATE_TRACKING] 			= (uint8_t)((tlv_buf[CFG_CMD13]- 0x30)*10+(tlv_buf[CFG_CMD14]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_RATE_PRESENCE] 			= (uint8_t)((tlv_buf[CFG_CMD15]- 0x30)*10+(tlv_buf[CFG_CMD16]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_HWAAS] 					= (uint8_t)((tlv_buf[CFG_CMD17]- 0x30)*10+(tlv_buf[CFG_CMD18]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_NBR_REMOVED_PC] 			= (uint8_t)((tlv_buf[CFG_CMD19]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_ITE_DEVIATION] 			= (uint8_t)((tlv_buf[CFG_CMD20]- 0x30)*10+(tlv_buf[CFG_CMD21]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_ITE_FAST_CUTOFF] 			= (uint8_t)((tlv_buf[CFG_CMD22]- 0x30)*10+(tlv_buf[CFG_CMD23]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_ITE_SLOW_CUTOFF] 			= (uint8_t)((tlv_buf[CFG_CMD24]- 0x30)*10+(tlv_buf[CFG_CMD25]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_ITR_TIME] 				= (uint8_t)((tlv_buf[CFG_CMD26]- 0x30)*10+(tlv_buf[CFG_CMD27]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_ITR_WEIGHT] 				= (uint8_t)((tlv_buf[CFG_CMD28]- 0x30)*10+(tlv_buf[CFG_CMD29]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_OUTPUT_TIME] 				= (uint8_t)((tlv_buf[CFG_CMD30]- 0x30)*10+(tlv_buf[CFG_CMD31]- 0x30));

							sts_cfg_nvm.p[RSS_CFG_DOWNSAMPLING_FACTOR] 		= (uint8_t)((tlv_buf[CFG_CMD32]- 0x30));
							sts_cfg_nvm.p[RSS_CFG_POWER_MODE] 				= (uint8_t)((tlv_buf[CFG_CMD33]- 0x30));



							if (invalid_flag == 0)
							{
								STS_PRESENCE_SENSOR_NVM_CFG();

								i=0;						// Step 1: Prepare status update message
								UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
								UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
								i = tlv_buf_size;
								APP_LOG(TS_OFF, VLEVEL_L, "###### RSS Full CFG=%s\r\n",(char *)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_RSS_FULL_SIZE;

								OnStoreSTSCFGContextRequest();
							}

					} else if (tlv_buf_size == CFG_CMD_RSS_SIMPLE_SIZE)
					{
							//Validation check
							invalid_flag = 0;
#if 1
							for (j =0; j < CFG_CMD_RSS_SIMPLE_SIZE-3; j++)
							{
								if ((tlv_buf[CFG_CMD4+j] >='0') && (tlv_buf[CFG_CMD4+j]<='9'))
								{
									//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]);
								} else {
									invalid_flag = 1;
								}
							}
#endif
							sts_cfg_nvm.p[RSS_CFG_START_M] 			= (uint8_t)((tlv_buf[CFG_CMD4] - 0x30)*10+(tlv_buf[CFG_CMD5] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_LENGTH_M] 		= (uint8_t)((tlv_buf[CFG_CMD6] - 0x30)*10+(tlv_buf[CFG_CMD7] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_THRESHOLD] 		= (uint8_t)((tlv_buf[CFG_CMD8] - 0x30)*10+(tlv_buf[CFG_CMD9] - 0x30));
							sts_cfg_nvm.p[RSS_CFG_RECEIVER_GAIN] 	= (uint8_t)((tlv_buf[CFG_CMD10]- 0x30)*10+(tlv_buf[CFG_CMD11]- 0x30));

							if (invalid_flag == 0)
							{
								STS_PRESENCE_SENSOR_NVM_CFG_SIMPLE();

								i=0;						// Step 1: Prepare status update message
								UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
								UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
								i = tlv_buf_size;
								APP_LOG(TS_OFF, VLEVEL_M, "###### RSS Simple CFG=%s\r\n",(char*)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_RSS_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");
				} else {

					// 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)
		//UTIL_MEM_cpy_8(sts_ac_code, tlv_buf+2,YUNHORN_STS_AC_CODE_SIZE);

		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))
		{
			//UTIL_MEM_cpy_8(sts_cfg_nvm.ac, sts_ac_code,YUNHORN_STS_AC_CODE_SIZE);

			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;
		UTIL_MEM_set_8((void*)outbuf, 0x0, sizeof(outbuf));
		UTIL_MEM_cpy_8((void*)outbuf,(void*)tlv_buf, tlv_buf_size);
		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];
		}
		//UTIL_MEM_cpy_8(AppData.Buffer, appDataBuffer, appBufferSize);

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

		APP_LOG(TS_OFF, VLEVEL_L, "###########Service Mask = %d  Buffer Size =%d \r\n", sts_service_mask, AppData.BufferSize);

	    if ((JoinLedTimer.IsRunning) && (LmHandlerJoinStatus() == LORAMAC_HANDLER_SET))
	    {
	      UTIL_TIMER_Stop(&JoinLedTimer);
#ifndef	STM32WLE5xx
	      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(STS_O7)||defined(STS_O5) || defined(STS_O6) || defined(STS_R0) || defined(STS_R5)|| defined(STS_R4)|| defined(STS_R1D))
	sts_cfg_nvm.length   = STS_O7_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.sts_ioc_mask;
	nvm_store_value[i++] = sts_cfg_nvm.length; //(uint8_t) STS_O7_NVM_CFG_SIZE; //sts_cfg_nvm.length;

	for (j = 0; j < STS_O7_CFG_PCFG_SIZE; j++) {
		nvm_store_value[i++] = (sts_cfg_nvm.p[j]);
	}
	nvm_store_value[i++] = sts_cfg_nvm.reserve02;
	nvm_store_value[i++] = sts_cfg_nvm.reserve03;
	nvm_store_value[i++] = sts_cfg_nvm.sensor_install_height_in_10cm;
	nvm_store_value[i++] = sts_cfg_nvm.alarm_parameter05;
	nvm_store_value[i++] = sts_cfg_nvm.alarm_mute_reset_timer_in_10sec;
	nvm_store_value[i++] = sts_cfg_nvm.alarm_lamp_bar_flashing_color;
	nvm_store_value[i++] = sts_cfg_nvm.occupancy_overtime_threshold_in_10min;
	nvm_store_value[i++] = sts_cfg_nvm.motionless_duration_threshold_in_min;
	nvm_store_value[i++] = sts_cfg_nvm.unconscious_or_motionless_level_threshold;
	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;

	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(void *cfg_in_nvm)
{
  /* USER CODE BEGIN OnRestoreSTSCFGContextRequest_1 */
//	uint8_t nvm_store_value[YUNHORN_STS_MAX_NVM_CFG_SIZE]="", nvm_store_size=YUNHORN_STS_MAX_NVM_CFG_SIZE;

	APP_LOG(TS_OFF, VLEVEL_M, "Restore NVM start\r\n");

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


	//memcpy(cfg_in_nvm, nvm_store_value, YUNHORN_STS_MAX_NVM_CFG_SIZE);

  /* 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);
	FLASH_IF_Read(nvm_stored_value, STS_CONFIG_NVM_BASE_ADDRESS, YUNHORN_STS_MAX_NVM_CFG_SIZE);
  /* USER CODE BEGIN OnRestoreContextRequest_Last */

//#if (defined(STS_O7)||defined(STS_O5) || defined(STS_O6) || defined(STS_R0) || defined(STS_R5)|| defined(STS_R4)|| defined(STS_R1D))
	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_M, "Initial 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]);
			sts_cfg_nvm.unit				= (uint8_t)(nvm_stored_value[NVM_UNIT]);
			sts_cfg_nvm.sampling			= (uint8_t)(nvm_stored_value[NVM_SAMPLING]);
			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.sts_ioc_mask			= (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_O7_CFG_PCFG_SIZE; j++) {						// P RSS CONFIG SIZE= 20U
				sts_cfg_nvm.p[j] 				= (uint8_t)nvm_stored_value[NVM_CFG_START+j];
			}
			sts_cfg_nvm.reserve02			=(uint8_t)nvm_stored_value[NVM_RESERVE02];
			sts_cfg_nvm.reserve03			=(uint8_t)nvm_stored_value[NVM_RESERVE03];
			sts_cfg_nvm.sensor_install_height_in_10cm			=(uint8_t)nvm_stored_value[NVM_SENSOR_INSTALL_HEIGHT];
			sts_cfg_nvm.alarm_parameter05			=(uint8_t)nvm_stored_value[NVM_ALARM_PARAMETER05];
			sts_cfg_nvm.alarm_mute_reset_timer_in_10sec = (uint8_t)nvm_stored_value[NVM_ALARM_MUTE_RESET_TIMER];
			sts_cfg_nvm.alarm_lamp_bar_flashing_color = (uint8_t)nvm_stored_value[NVM_ALARM_LAMP_BAR_FLASHING_COLOR];
			sts_cfg_nvm.occupancy_overtime_threshold_in_10min = (uint8_t)nvm_stored_value[NVM_OCCUPANCY_OVERTIME_THRESHOLD];
			sts_cfg_nvm.motionless_duration_threshold_in_min= (uint8_t)nvm_stored_value[NVM_MOTIONLESS_DURATION_THRESHOLD];
			sts_cfg_nvm.unconscious_or_motionless_level_threshold 		= (uint8_t)nvm_stored_value[NVM_UNCONSCIOUS_LEVEL_THRESHOLD];
			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);
	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 *= 1000;	// to ms

	uint32_t sampling = (sts_cfg_nvm.sampling);
	if ((char)sts_cfg_nvm.s_unit =='M') {
		sampling *= 60;
	} else if ((char) sts_cfg_nvm.s_unit =='H') {
		sampling *= 3600;
	} else if ((char) sts_cfg_nvm.s_unit =='S') {
		sampling *= 1;
	}
	sampling= sampling*1000;  // to ms

	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);
		OnYunhornSTSSamplingPeriodicityChanged(sampling);				// in m-sec unit
	} else
	{
		//OnTxPeriodicityChanged(TxPeriodicity);				// in msec unit
		//Heart-beat or Sampling interval
#ifdef 	STS_E0
		samplingperiodicity = (samplingperiodicity > 0)? samplingperiodicity : 1;		// in seconds unit
		HeartBeatPeriodicity = samplingperiodicity*1000;
#endif
#if	defined(STS_O7)|| defined(STS_O6) ||defined(STS_O5)
		HeartBeatPeriodicity = periodicity;
		OnYunhornSTSHeartBeatPeriodicityChanged(HeartBeatPeriodicity);
		OnYunhornSTSSamplingPeriodicityChanged(sampling);				// 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_lamp_bar_color 							= STS_GREEN;
	sts_service_mask 							= sts_cfg_nvm.sts_service_mask;
	sts_lamp_bar_flashing_color 				= sts_cfg_nvm.alarm_lamp_bar_flashing_color;
	if (sts_work_mode == STS_UNI_MODE){
		sts_presence_fall_detection =TRUE;
	} else {
		sts_presence_fall_detection =FALSE;
	}
	sts_sensor_install_height								= sts_cfg_nvm.sensor_install_height_in_10cm*100;		//10cm translate to mm
	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_fall_confirm_threshold_in_10sec						= (uint8_t)sts_cfg_nvm.fall_confirm_threshold_in_10sec*10;

	sts_unconscious_or_motionless_level_threshold 			= (uint8_t)(sts_cfg_nvm.unconscious_or_motionless_level_threshold+1)*128; 			// unconscious threshold
	sts_motionless_duration_threshold_in_min				= (uint8_t)sts_cfg_nvm.motionless_duration_threshold_in_min;
	sts_occupancy_overtime_threshold_in_10min 				= (uint8_t)sts_cfg_nvm.occupancy_overtime_threshold_in_10min;			// minutes


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


#if	defined(STS_O7)||defined(STS_O6)
//	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
	//STS_SENSOR_Distance_Test_Process();
	//APP_LOG(TS_OFF, VLEVEL_L, "\r\nDistance Measured =%u mm, Ceiling Height=%u \r\n", (uint16_t)sts_distance_rss_distance, (uint16_t)sts_sensor_install_height);

}

void STS_SENSOR_Distance_Test_Process(void)
{
#if	defined(STS_O6)||defined(STS_O7)
		STS_PRESENCE_SENSOR_Distance_Measure_Process();
#endif

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

void STS_SENSOR_Function_Test_Process(void)
{
	char tstbuf[128] =""; uint8_t i=0, count = 1;
	uint8_t mems_Dev_ID[2] = {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));

	APP_LOG(TS_OFF, VLEVEL_H, "\r\nStart Function Test \r\n");

	STS_SENSOR_MEMS_Get_ID(mems_Dev_ID);
	if ((mems_Dev_ID[0]==0X0) && (mems_Dev_ID[1]==0x0)) {
		tstbuf[i++] = (uint8_t) 'X'; // Slave MEMS Not Avaliable
	}
	else
	{
		tstbuf[i++] = (uint8_t)14; //length of following data
#if	defined(STS_O7)||defined(STS_O6)
		uint8_t self_test_result[10]={0x0};

		STS_PRESENCE_SENSOR_Function_Test_Process(self_test_result, 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();
		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));
	UTIL_MEM_cpy_8((void*)outbuf, (void*)tstbuf, i);
	STS_SENSOR_Upload_Message(LORAWAN_USER_APP_CTRL_REPLY_PORT, i, (uint8_t*)outbuf);

}

uint32_t STS_Get_Date_Time_Stamp(void)
{
	struct tm localtime;

	SysTime_t UnixEpoch = SysTimeGet();
	UnixEpoch.Seconds -= 18; /*removing leap seconds*/

	SysTimeLocalTime(UnixEpoch.Seconds,  &localtime);
	APP_LOG(TS_OFF, VLEVEL_M, "UTC TIME:%02dh%02dm%02ds on %02d/%02d/%04d\r\n",
						            localtime.tm_hour, localtime.tm_min, localtime.tm_sec,
						            localtime.tm_mday, localtime.tm_mon + 1, localtime.tm_year + 1900);

	return (uint32_t)UnixEpoch.Seconds;
}